home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / lib.js < prev    next >
Text File  |  2010-01-15  |  170KB  |  6,773 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. var FBL = fbXPCOMUtils;
  4.  
  5. try { /*@explore*/
  6.  
  7. (function() {
  8.  
  9. // ************************************************************************************************
  10. // Modules
  11.  
  12. Components.utils.import("resource://gre/modules/PluralForm.jsm");
  13.  
  14. // ************************************************************************************************
  15. // Constants
  16.  
  17. const Cc = Components.classes;
  18. const Ci = Components.interfaces;
  19.  
  20. this.fbs = Cc["@joehewitt.com/firebug;1"].getService().wrappedJSObject;
  21. this.httpObserver = this.CCSV("@joehewitt.com/firebug-http-observer;1", "nsIObserverService");
  22. this.jsd = this.CCSV("@mozilla.org/js/jsd/debugger-service;1", "jsdIDebuggerService");
  23.  
  24. const finder = this.finder = this.CCIN("@mozilla.org/embedcomp/rangefind;1", "nsIFind");
  25. const wm = this.CCSV("@mozilla.org/appshell/window-mediator;1", "nsIWindowMediator");
  26. const ioService = this.CCSV("@mozilla.org/network/io-service;1", "nsIIOService");
  27. const consoleService = Components.classes["@mozilla.org/consoleservice;1"].
  28.     getService(Components.interfaces["nsIConsoleService"]);
  29.  
  30. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  31.  
  32. const reNotWhitespace = /[^\s]/;
  33. const reSplitFile = /:\/{1,3}(.*?)\/([^\/]*?)\/?($|\?.*)/;
  34. const reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;  // This RE and the previous one should changed to be consistent
  35. const reChromeCase = /chrome:\/\/([^/]*)\/(.*?)$/;
  36. // Globals
  37. this.reDataURL = /data:text\/javascript;fileName=([^;]*);baseLineNumber=(\d*?),((?:.*?%0A)|(?:.*))/g;
  38. this.reJavascript = /\s*javascript:\s*(.*)/;
  39. this.reChrome = /chrome:\/\/([^\/]*)\//;
  40. this.reCSS = /\.css$/;
  41. this.reFile = /file:\/\/([^\/]*)\//;
  42. this.reUpperCase = /[A-Z]/;
  43.  
  44. const reSplitLines = /\r\n|\r|\n/;
  45. const reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/;
  46. const reGuessFunction = /['"]?([$0-9A-Za-z_]+)['"]?\s*[:=]\s*(function|eval|new Function)/;
  47. const reWord = /([A-Za-z_$][A-Za-z_$0-9]*)(\.([A-Za-z_$][A-Za-z_$0-9]*))*/;
  48.  
  49. const overrideDefaultsWithPersistedValuesTimeout = 500;
  50.  
  51. const NS_SEEK_SET = Ci.nsISeekableStream.NS_SEEK_SET;
  52.  
  53. // ************************************************************************************************
  54. // Namespaces
  55.  
  56. var namespaces = [];
  57.  
  58. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  59.  
  60. this.ns = function(fn)
  61. {
  62.     var ns = {};
  63.     namespaces.push(fn, ns);
  64.     return ns;
  65. };
  66.  
  67. this.initialize = function()
  68. {
  69.     for (var i = 0; i < namespaces.length; i += 2)
  70.     {
  71.         var fn = namespaces[i];
  72.         var ns = namespaces[i+1];
  73.         fn.apply(ns);
  74.     }
  75.  
  76. };
  77.  
  78. // ************************************************************************************************
  79. // Basics
  80.  
  81. this.bind = function()  // fn, thisObject, args => thisObject.fn(args, arguments);
  82. {
  83.    var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  84.    return function() { return fn.apply(object, arrayInsert(cloneArray(args), 0, arguments)); }
  85. };
  86.  
  87. this.bindFixed = function() // fn, thisObject, args => thisObject.fn(args);
  88. {
  89.     var args = cloneArray(arguments), fn = args.shift(), object = args.shift();
  90.     return function() { return fn.apply(object, args); }
  91. };
  92.  
  93. this.extend = function(l, r)
  94. {
  95.     var newOb = {};
  96.     for (var n in l)
  97.         newOb[n] = l[n];
  98.     for (var n in r)
  99.         newOb[n] = r[n];
  100.     return newOb;
  101. };
  102.  
  103. this.descend = function(prototypeParent, childProperties)
  104. {
  105.     function protoSetter() {};
  106.     protoSetter.prototype = prototypeParent;
  107.     var newOb = new protoSetter();
  108.     for (var n in childProperties)
  109.         newOb[n] = childProperties[n];
  110.     return newOb;
  111. };
  112.  
  113. // ************************************************************************************************
  114. // Arrays
  115.  
  116. this.keys = function(map)  // At least sometimes the keys will be on user-level window objects
  117. {
  118.     var keys = [];
  119.     try
  120.     {
  121.         for (var name in map)  // enumeration is safe
  122.             keys.push(name);   // name is string, safe
  123.     }
  124.     catch (exc)
  125.     {
  126.         // Sometimes we get exceptions trying to iterate properties
  127.     }
  128.  
  129.     return keys;  // return is safe
  130. };
  131.  
  132. this.values = function(map)
  133. {
  134.     var values = [];
  135.     try
  136.     {
  137.         for (var name in map)
  138.         {
  139.             try
  140.             {
  141.                 values.push(map[name]);
  142.             }
  143.             catch (exc)
  144.             {
  145.                 // Sometimes we get exceptions trying to access properties
  146.             }
  147.  
  148.         }
  149.     }
  150.     catch (exc)
  151.     {
  152.         // Sometimes we get exceptions trying to iterate properties
  153.     }
  154.  
  155.     return values;
  156. };
  157.  
  158. this.remove = function(list, item)
  159. {
  160.     for (var i = 0; i < list.length; ++i)
  161.     {
  162.         if (list[i] == item)
  163.         {
  164.             list.splice(i, 1);
  165.             break;
  166.         }
  167.     }
  168. };
  169.  
  170. this.sliceArray = function(array, index)
  171. {
  172.     var slice = [];
  173.     for (var i = index; i < array.length; ++i)
  174.         slice.push(array[i]);
  175.  
  176.     return slice;
  177. };
  178.  
  179. function cloneArray(array, fn)
  180. {
  181.    var newArray = [];
  182.  
  183.    if (fn)
  184.        for (var i = 0; i < array.length; ++i)
  185.            newArray.push(fn(array[i]));
  186.    else
  187.        for (var i = 0; i < array.length; ++i)
  188.            newArray.push(array[i]);
  189.  
  190.    return newArray;
  191. }
  192.  
  193. function extendArray(array, array2)
  194. {
  195.    var newArray = [];
  196.    newArray.push.apply(newArray, array);
  197.    newArray.push.apply(newArray, array2);
  198.    return newArray;
  199. }
  200.  
  201. this.extendArray = extendArray;
  202. this.cloneArray = cloneArray;
  203.  
  204. function arrayInsert(array, index, other)
  205. {
  206.    for (var i = 0; i < other.length; ++i)
  207.        array.splice(i+index, 0, other[i]);
  208.  
  209.    return array;
  210. }
  211.  
  212. this.arrayInsert = arrayInsert;
  213.  
  214. // ************************************************************************************************
  215.  
  216. this.safeToString = function(ob)
  217. {
  218.     try
  219.     {
  220.         if (!ob)
  221.         {
  222.             if (ob == undefined)
  223.                 return 'undefined';
  224.             if (ob == null)
  225.                 return 'null';
  226.             if (ob == false)
  227.                 return 'false';
  228.             return "";
  229.         }
  230.         if (ob && (typeof (ob['toString']) == "function") )
  231.             return ob.toString();
  232.         if (ob && typeof (ob['toSource']) == 'function')
  233.             return ob.toSource();
  234.        /* https://bugzilla.mozilla.org/show_bug.cgi?id=522590 */
  235.         var str = "[";
  236.         for (var p in ob)
  237.             str += p+',';
  238.         return str + ']';
  239.  
  240.     }
  241.     catch (exc)
  242.     {
  243.     }
  244.     return "[unsupported: no toString() function in type "+typeof(ob)+"]";
  245. };
  246.  
  247. // ************************************************************************************************
  248.  
  249. this.hasProperties = function(ob)
  250. {
  251.     try
  252.     {
  253.         for (var name in ob)
  254.             return true;
  255.     } catch (exc) {}
  256.     return false;
  257. };
  258.  
  259. // ************************************************************************************************
  260.  
  261. this.convertToUnicode = function(text, charset)
  262. {
  263.     if (!text)
  264.         return "";
  265.  
  266.     try
  267.     {
  268.         var conv = this.CCSV("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
  269.         conv.charset = charset ? charset : "UTF-8";
  270.         return conv.ConvertToUnicode(text);
  271.     }
  272.     catch (exc)
  273.     {
  274.         throw new Error("Firebug failed to convert to unicode using charset: "+conv.charset+" in @mozilla.org/intl/scriptableunicodeconverter");
  275.     }
  276. };
  277.  
  278. this.convertFromUnicode = function(text, charset)
  279. {
  280.     if (!text)
  281.         return "";
  282.  
  283.     try
  284.     {
  285.         var conv = this.CCSV("@mozilla.org/intl/scriptableunicodeconverter", "nsIScriptableUnicodeConverter");
  286.         conv.charset = charset ? charset : "UTF-8";
  287.         return conv.ConvertFromUnicode(text);
  288.     }
  289.     catch (exc)
  290.     {
  291.     }
  292. };
  293.  
  294. this.getPlatformName = function()
  295. {
  296.     return this.CCSV("@mozilla.org/xre/app-info;1", "nsIXULRuntime").OS;
  297. };
  298.  
  299. this.beep = function()
  300. {
  301.     var sounder = this.CCSV("@mozilla.org/sound;1", "nsISound");
  302.     sounder.beep();
  303. };
  304.  
  305. this.getUniqueId = function() {
  306.     return this.getRandomInt(0,65536);
  307. }
  308.  
  309. this.getRandomInt = function(min, max) {
  310.   return Math.floor(Math.random() * (max - min + 1) + min);
  311. }
  312.  
  313. // ************************************************************************************************
  314.  
  315. this.createStyleSheet = function(doc, url)
  316. {
  317.     var style = doc.createElementNS("http://www.w3.org/1999/xhtml", "style");
  318.     style.setAttribute("charset","utf-8");
  319.     style.setAttribute("type", "text/css");
  320.     style.innerHTML = this.getResource(url);
  321.     FBL.unwrapObject(style).firebugIgnore = true;
  322.     return style;
  323. }
  324.  
  325. this.addStyleSheet = function(doc, style)
  326. {
  327.     var heads = doc.getElementsByTagName("head");
  328.     if (heads.length)
  329.         heads[0].appendChild(style);
  330.     else
  331.         doc.documentElement.appendChild(style);
  332. };
  333.  
  334. this.addScript = function(doc, id, src)
  335. {
  336.     var element = doc.createElementNS("http://www.w3.org/1999/xhtml", "html:script");
  337.     element.setAttribute("type", "text/javascript");
  338.     element.setAttribute("id", id);
  339.     if (!FBTrace.DBG_CONSOLE)
  340.         FBL.unwrapObject(element).firebugIgnore = true;
  341.  
  342.     element.innerHTML = src;
  343.     if (doc.documentElement)
  344.         doc.documentElement.appendChild(element);
  345.     else
  346.     {
  347.         // See issue 1079, the svg test case gives this error
  348.     }
  349.     return element;
  350. }
  351.  
  352. // ************************************************************************************************
  353.  
  354. this.isAncestorIgnored = function(node)
  355. {
  356.     for (var parent = node; parent; parent = parent.parentNode)
  357.     {
  358.         if (FBL.unwrapObject(parent).firebugIgnore)
  359.             return true;
  360.     }
  361.  
  362.     return false;
  363. }
  364.  
  365. // ************************************************************************************************
  366. // Localization
  367.  
  368. /*
  369.  * $STR - intended for localization of a static string.
  370.  * $STRF - intended for localization of a string with dynamically inserted values.
  371.  * $STRP - intended for localization of a string with dynamically plural forms.
  372.  *
  373.  * Notes:
  374.  * 1) Name with _ in place of spaces is the key in the firebug.properties file.
  375.  * 2) If the specified key isn't localized for particular language, both methods use
  376.  *    the part after the last dot (in the specified name) as the return value.
  377.  *
  378.  * Examples:
  379.  * $STR("Label"); - search for key "Label" within the firebug.properties file
  380.  *                 and returns its value. If the key doesn't exist returns "Label".
  381.  *
  382.  * $STR("Button Label"); - search for key "Button_Label" withing the firebug.properties
  383.  *                        file. If the key doesn't exist returns "Button Label".
  384.  *
  385.  * $STR("net.Response Header"); - search for key "net.Response_Header". If the key doesn't
  386.  *                               exist returns "Response Header".
  387.  *
  388.  * firebug.properties:
  389.  * net.timing.Request_Time=Request Time: %S [%S]
  390.  *
  391.  * var param1 = 10;
  392.  * var param2 = "ms";
  393.  * $STRF("net.timing.Request Time", param1, param2);  -> "Request Time: 10 [ms]"
  394.  *
  395.  * - search for key "net.timing.Request_Time" within the firebug.properties file. Parameters
  396.  *   are inserted at specified places (%S) in the same order as they are passed. If the
  397.  *   key doesn't exist the method returns "Request Time".
  398.  */
  399. function $STR(name, bundle)
  400. {
  401.     try
  402.     {
  403.         if (typeof bundle == "string")
  404.             bundle = document.getElementById(bundle);
  405.  
  406.         if (bundle)
  407.             return bundle.getString(name.replace(' ', '_', "g"));
  408.  
  409.         if (Firebug)
  410.             return Firebug.getStringBundle().GetStringFromName(name.replace(' ', '_', "g"));
  411.     }
  412.     catch (err)
  413.     {
  414.     }
  415.  
  416.     // XXXjjb apparently we get to this code if we get an exception above...is that best we can do?
  417.  
  418.     // Use only the label after last dot.
  419.     var index = name.lastIndexOf(".");
  420.     if (index > 0 && name.charAt(index-1) != "\\")
  421.         name = name.substr(index + 1);
  422.     name = name.replace("_", " ");
  423.  
  424.     return name;
  425. }
  426.  
  427. function $STRF(name, args, bundle)
  428. {
  429.     try
  430.     {
  431.         // xxxHonza: Workaround for #485511
  432.         if (!bundle)
  433.             bundle = "strings_firebug";
  434.  
  435.         if (typeof bundle == "string")
  436.             bundle = document.getElementById(bundle);
  437.  
  438.         if (bundle)
  439.             return bundle.getFormattedString(name.replace(' ', '_', "g"), args);
  440.         else
  441.             return Firebug.getStringBundle().formatStringFromName(name.replace(' ', '_', "g"), args, args.length);
  442.     }
  443.     catch (err)
  444.     {
  445.     }
  446.  
  447.     // Use only the label after last dot.
  448.     var index = name.lastIndexOf(".");
  449.     if (index > 0)
  450.         name = name.substr(index + 1);
  451.  
  452.     return name;
  453. }
  454.  
  455. function $STRP(name, args, index, bundle)
  456. {
  457.     // xxxHonza:
  458.     // pluralRule from chrome://global/locale/intl.properties for Chinese is 1,
  459.     // which is wrong, it should be 0.
  460.  
  461.     var getPluralForm = PluralForm.get;
  462.     var getNumForms = PluralForm.numForms;
  463.  
  464.     // Get custom plural rule; otherwise the rule from chrome://global/locale/intl.properties
  465.     // (depends on the current locale) is used.
  466.     var pluralRule = Firebug.getPluralRule();
  467.     if (!isNaN(parseInt(pluralRule, 10)))
  468.         [getPluralForm, getNumForms] = PluralForm.makeGetter(pluralRule);
  469.  
  470.     // Index of the argument with plural form (there must be only one arg that needs plural form).
  471.     if (!index)
  472.         index = 0;
  473.  
  474.     var margs = [];
  475.     var numForms = getNumForms();
  476.  
  477.     // Repeat the args for numForms time(s)
  478.     for (var i = 0; i < numForms; i++)
  479.         margs = margs.concat(args);
  480.  
  481.     // Get proper plural form from the string (depends on the current Firefox locale).
  482.     var translatedString = $STRF(name, margs, bundle);
  483.     if (translatedString.search(";") > 0)
  484.         return getPluralForm(args[index], translatedString);
  485.  
  486.     // translatedString contains no ";", either rule 0 or getString fails
  487.     return translatedString;
  488. }
  489.  
  490. this.$STR = $STR;
  491. this.$STRF = $STRF;
  492. this.$STRP = $STRP;
  493.  
  494. /*
  495.  * Use the current value of the attribute as a key to look up the localized value.
  496.  */
  497. this.internationalize = function(element, attr, args)
  498. {
  499.     if (typeof element == "string")
  500.         element = document.getElementById(element);
  501.  
  502.     if (element)
  503.     {
  504.         var xulString = element.getAttribute(attr);
  505.         if (xulString)
  506.         {
  507.             var localized = args ? $STRF(xulString, args) : $STR(xulString);
  508.  
  509.             // Set localized value of the attribute.
  510.             element.setAttribute(attr, localized);
  511.         }
  512.     }
  513.     else
  514.     {
  515.     }
  516. }
  517.  
  518. // ************************************************************************************************
  519. // Visibility
  520.  
  521. this.isVisible = function(elt)
  522. {
  523.     if (isElementXUL(elt))
  524.     {
  525.         //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+ elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
  526.         return (!elt.hidden && !elt.collapsed);
  527.     }
  528.     return elt.offsetWidth > 0 || elt.offsetHeight > 0 || elt.localName in invisibleTags || isElementSVG(elt) || isElementMathML(elt);
  529. };
  530.  
  531. this.collapse = function(elt, collapsed)
  532. {
  533.     elt.setAttribute("collapsed", collapsed ? "true" : "false");
  534. };
  535.  
  536. this.obscure = function(elt, obscured)
  537. {
  538.     if (obscured)
  539.         this.setClass(elt, "obscured");
  540.     else
  541.         this.removeClass(elt, "obscured");
  542. };
  543.  
  544. this.hide = function(elt, hidden)
  545. {
  546.     elt.style.visibility = hidden ? "hidden" : "visible";
  547. };
  548.  
  549. this.clearNode = function(node)
  550. {
  551.     this.clearDomplate(node);
  552.     node.innerHTML = "";
  553. };
  554.  
  555. this.eraseNode = function(node)
  556. {
  557.     this.clearDomplate(node);
  558.     while (node.lastChild)
  559.         node.removeChild(node.lastChild);
  560. };
  561.  
  562. this.clearDomplate = function(node)
  563. {
  564.     if (!Firebug.clearDomplate)
  565.         return;
  566.  
  567.     var walker = node.ownerDocument.createTreeWalker(node,
  568.         Ci.nsIDOMNodeFilter.SHOW_ALL, null, true);
  569.  
  570.     while (node)
  571.     {
  572.         if (node.repObject)
  573.             node.repObject = null;
  574.  
  575.         if (node.stackTrace)
  576.             node.stackTrace = null;
  577.  
  578.         if (node.checked)
  579.             node.checked = null;
  580.  
  581.         if (node.domObject)
  582.             node.domObject = null;
  583.  
  584.         if (node.toggles)
  585.             node.toggles = null;
  586.  
  587.         if (node.domPanel)
  588.             node.domPanel = null;
  589.  
  590.         node = walker.nextNode();
  591.     }
  592. }
  593.  
  594. // ************************************************************************************************
  595. // Window iteration
  596.  
  597. this.iterateWindows = function(win, handler)
  598. {
  599.     if (!win || !win.document)
  600.         return;
  601.  
  602.     handler(win);
  603.  
  604.     if (win == top || !win.frames) return; // XXXjjb hack for chromeBug
  605.  
  606.     for (var i = 0; i < win.frames.length; ++i)
  607.     {
  608.         var subWin = win.frames[i];
  609.         if (subWin != win)
  610.             this.iterateWindows(subWin, handler);
  611.     }
  612. };
  613.  
  614. this.getRootWindow = function(win)
  615. {
  616.     for (; win; win = win.parent)
  617.     {
  618.         if (!win.parent || win == win.parent || !(win.parent instanceof Window) )
  619.             return win;
  620.     }
  621.     return null;
  622. };
  623.  
  624. // ************************************************************************************************
  625. // CSS classes
  626.  
  627. var classNameReCache={};
  628. this.hasClass = function(node, name)
  629. {
  630.     if (!node || node.nodeType != 1 || !node.className || name == '')
  631.         return false;
  632.  
  633.     if (name.indexOf(" ") != -1)
  634.     {
  635.         var classes = name.split(" "), len = classes.length, found=false;
  636.         for (var i = 0; i < len; i++)
  637.         {
  638.             var cls = classes[i].trim();
  639.             if (cls != "")
  640.             {
  641.                 if (this.hasClass(node, cls) == false)
  642.                     return false;
  643.                 found = true;
  644.             }
  645.         }
  646.         return found;
  647.     }
  648.  
  649.     var re;
  650.     if (name.indexOf("-") == -1)
  651.         re = classNameReCache[name] = classNameReCache[name] || new RegExp('(^|\\s)' + name + '(\\s|$)', "g");
  652.     else // XXXsroussey don't cache these, they are often setting values. Should be using setUserData/getUserData???
  653.         re = new RegExp('(^|\\s)' + name + '(\\s|$)', "g")
  654.     return node.className.search(re) != -1;
  655. };
  656.  
  657. this.setClass = function(node, name)
  658. {
  659.     if (!node || node.nodeType != 1 || name == '')
  660.         return;
  661.  
  662.     if (name.indexOf(" ") != -1)
  663.     {
  664.         var classes = name.split(" "), len = classes.length;
  665.         for (var i = 0; i < len; i++)
  666.         {
  667.             var cls = classes[i].trim();
  668.             if (cls != "")
  669.             {
  670.                 this.setClass(node, cls);
  671.             }
  672.         }
  673.         return;
  674.     }
  675.     if (!this.hasClass(node, name))
  676.         node.className = node.className.trim() + " " + name;
  677. };
  678.  
  679. this.getClassValue = function(node, name)
  680. {
  681.     var re = new RegExp(name+"-([^ ]+)");
  682.     var m = re.exec(node.className);
  683.     return m ? m[1] : "";
  684. };
  685.  
  686. this.removeClass = function(node, name)
  687. {
  688.     if (!node || node.nodeType != 1 || node.className == '' || name == '')
  689.         return;
  690.  
  691.     if (name.indexOf(" ") != -1)
  692.     {
  693.         var classes = name.split(" "), len = classes.length;
  694.         for (var i = 0; i < len; i++)
  695.         {
  696.             var cls = classes[i].trim();
  697.             if (cls != "")
  698.             {
  699.                 if (this.hasClass(node, cls) == false)
  700.                     this.removeClass(node, cls);
  701.             }
  702.         }
  703.         return;
  704.     }
  705.  
  706.     var re;
  707.     if (name.indexOf("-") == -1)
  708.         re = classNameReCache[name] = classNameReCache[name] || new RegExp('(^|\\s)' + name + '(\\s|$)', "g");
  709.     else // XXXsroussey don't cache these, they are often setting values. Should be using setUserData/getUserData???
  710.         re = new RegExp('(^|\\s)' + name + '(\\s|$)', "g")
  711.  
  712.     node.className = node.className.replace(re, " ");
  713.  
  714. };
  715.  
  716. this.toggleClass = function(elt, name)
  717. {
  718.     if (this.hasClass(elt, name))
  719.         this.removeClass(elt, name);
  720.     else
  721.         this.setClass(elt, name);
  722. };
  723.  
  724. this.setClassTimed = function(elt, name, context, timeout)
  725. {
  726.     if (!timeout)
  727.         timeout = 1300;
  728.  
  729.     if (elt.__setClassTimeout)
  730.         context.clearTimeout(elt.__setClassTimeout);
  731.     else
  732.         this.setClass(elt, name);
  733.  
  734.     if (!this.isVisible(elt))
  735.     {
  736.         if (elt.__invisibleAtSetPoint)
  737.             elt.__invisibleAtSetPoint--;
  738.         else
  739.             elt.__invisibleAtSetPoint = 5;
  740.     }
  741.     else
  742.     {
  743.         delete elt.__invisibleAtSetPoint;
  744.     }
  745.  
  746.     elt.__setClassTimeout = context.setTimeout(function()
  747.     {
  748.         delete elt.__setClassTimeout;
  749.  
  750.         if (elt.__invisibleAtSetPoint)
  751.             FBL.setClassTimed(elt, name, context, timeout);
  752.         else
  753.         {
  754.             delete elt.__invisibleAtSetPoint;
  755.             FBL.removeClass(elt, name);
  756.         }
  757.     }, timeout);
  758. };
  759.  
  760. this.cancelClassTimed = function(elt, name, context)
  761. {
  762.     if (elt.__setClassTimeout)
  763.     {
  764.         FBL.removeClass(elt, name);
  765.         context.clearTimeout(elt.__setClassTimeout);
  766.         delete elt.__setClassTimeout;
  767.     }
  768. };
  769.  
  770. // ************************************************************************************************
  771. // DOM queries
  772.  
  773. this.$ = function(id, doc)
  774. {
  775.     if (doc)
  776.         return doc.getElementById(id);
  777.     else
  778.         return document.getElementById(id);
  779. };
  780.  
  781. this.getChildByClass = function(node) // ,classname, classname, classname...
  782. {
  783.     for (var i = 1; i < arguments.length; ++i)
  784.     {
  785.         var className = arguments[i];
  786.         var child = node.firstChild;
  787.         node = null;
  788.         for (; child; child = child.nextSibling)
  789.         {
  790.             if (this.hasClass(child, className))
  791.             {
  792.                 node = child;
  793.                 break;
  794.             }
  795.         }
  796.     }
  797.  
  798.     return node;
  799. };
  800.  
  801. this.getAncestorByClass = function(node, className)
  802. {
  803.     for (var parent = node; parent; parent = parent.parentNode)
  804.     {
  805.         if (this.hasClass(parent, className))
  806.             return parent;
  807.     }
  808.  
  809.     return null;
  810. };
  811.  
  812. /* @Deprecated  Use native Firefox: node.getElementsByClassName(names).item(0) */
  813. this.getElementByClass = function(node, className)  // className, className, ...
  814. {
  815.     return FBL.getElementsByClass.apply(this,arguments).item(0);
  816. };
  817.  
  818. /* @Deprecated  Use native Firefox: node.getElementsByClassName(names) */
  819. this.getElementsByClass = function(node, className)  // className, className, ...
  820. {
  821.     var args = cloneArray(arguments); args.splice(0, 1);
  822.     var className = args.join(" ");
  823.     return node.getElementsByClassName(className);
  824. };
  825.  
  826. this.getElementsByAttribute = function(node, attrName, attrValue)
  827. {
  828.     function iteratorHelper(node, attrName, attrValue, result)
  829.     {
  830.         for (var child = node.firstChild; child; child = child.nextSibling)
  831.         {
  832.             if (child.getAttribute(attrName) == attrValue)
  833.                 result.push(child);
  834.  
  835.             iteratorHelper(child, attrName, attrValue, result);
  836.         }
  837.     }
  838.  
  839.     var result = [];
  840.     iteratorHelper(node, attrName, attrValue, result);
  841.     return result;
  842. }
  843.  
  844. this.isAncestor = function(node, potentialAncestor)
  845. {
  846.     for (var parent = node; parent; parent = parent.parentNode)
  847.     {
  848.         if (parent == potentialAncestor)
  849.             return true;
  850.     }
  851.  
  852.     return false;
  853. };
  854.  
  855. this.getNextElement = function(node)
  856. {
  857.     while (node && node.nodeType != 1)
  858.         node = node.nextSibling;
  859.  
  860.     return node;
  861. };
  862.  
  863. this.getPreviousElement = function(node)
  864. {
  865.     while (node && node.nodeType != 1)
  866.         node = node.previousSibling;
  867.  
  868.     return node;
  869. };
  870.  
  871. this.getBody = function(doc)
  872. {
  873.     if (doc.body)
  874.         return doc.body;
  875.  
  876.     var body = doc.getElementsByTagName("body")[0];
  877.     if (body)
  878.         return body;
  879.  
  880.     return doc.documentElement;  // For non-HTML docs
  881. };
  882.  
  883. this.findNextDown = function(node, criteria)
  884. {
  885.     if (!node)
  886.         return null;
  887.  
  888.     for (var child = node.firstChild; child; child = child.nextSibling)
  889.     {
  890.         if (criteria(child))
  891.             return child;
  892.  
  893.         var next = this.findNextDown(child, criteria);
  894.         if (next)
  895.             return next;
  896.     }
  897. };
  898.  
  899. this.findPreviousUp = function(node, criteria)
  900. {
  901.     if (!node)
  902.         return null;
  903.  
  904.     for (var child = node.lastChild; child; child = child.previousSibling)
  905.     {
  906.         var next = this.findPreviousUp(child, criteria);
  907.         if (next)
  908.             return next;
  909.  
  910.         if (criteria(child))
  911.             return child;
  912.     }
  913. };
  914.  
  915. this.findNext = function(node, criteria, upOnly, maxRoot)
  916. {
  917.     if (!node)
  918.         return null;
  919.  
  920.     if (!upOnly)
  921.     {
  922.         var next = this.findNextDown(node, criteria);
  923.         if (next)
  924.             return next;
  925.     }
  926.  
  927.     for (var sib = node.nextSibling; sib; sib = sib.nextSibling)
  928.     {
  929.         if (criteria(sib))
  930.             return sib;
  931.  
  932.         var next = this.findNextDown(sib, criteria);
  933.         if (next)
  934.             return next;
  935.     }
  936.  
  937.     if (node.parentNode && node.parentNode != maxRoot)
  938.         return this.findNext(node.parentNode, criteria, true);
  939. };
  940.  
  941. this.findPrevious = function(node, criteria, downOnly, maxRoot)
  942. {
  943.     if (!node)
  944.         return null;
  945.  
  946.     for (var sib = node.previousSibling; sib; sib = sib.previousSibling)
  947.     {
  948.         var prev = this.findPreviousUp(sib, criteria);
  949.         if (prev)
  950.             return prev;
  951.  
  952.         if (criteria(sib))
  953.             return sib;
  954.     }
  955.  
  956.     if (!downOnly)
  957.     {
  958.         var next = this.findPreviousUp(node, criteria);
  959.         if (next)
  960.             return next;
  961.     }
  962.  
  963.     if (node.parentNode && node.parentNode != maxRoot)
  964.     {
  965.         if (criteria(node.parentNode))
  966.             return node.parentNode;
  967.  
  968.         return this.findPrevious(node.parentNode, criteria, true);
  969.     }
  970. };
  971.  
  972. this.getNextByClass = function(root, state)
  973. {
  974.     function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
  975.     return this.findNext(root, iter);
  976. };
  977.  
  978. this.getPreviousByClass = function(root, state)
  979. {
  980.     function iter(node) { return node.nodeType == 1 && FBL.hasClass(node, state); }
  981.     return this.findPrevious(root, iter);
  982. };
  983.  
  984. this.hasChildElements = function(node)
  985. {
  986.     if (node.contentDocument) // iframes
  987.         return true;
  988.  
  989.     for (var child = node.firstChild; child; child = child.nextSibling)
  990.     {
  991.         if (child.nodeType == 1)
  992.             return true;
  993.     }
  994.  
  995.     return false;
  996. };
  997.  
  998. this.isElement = function(o)
  999. {
  1000.     try {
  1001.         return o && o instanceof Element;
  1002.     }
  1003.     catch (ex) {
  1004.         return false;
  1005.     }
  1006. };
  1007.  
  1008. this.isNode = function(o)
  1009. {
  1010.     try {
  1011.         return o && o instanceof Node;
  1012.     }
  1013.     catch (ex) {
  1014.         return false;
  1015.     }
  1016. };
  1017.  
  1018. this.XW_instanceof = function(obj, type) // Cross Window instanceof; type is local to this window
  1019. {
  1020.     if (obj instanceof type)
  1021.         return true;  // within-window test
  1022.  
  1023.     if (!type)
  1024.         return false;
  1025.     if (!obj)
  1026.         return (type == "undefined");
  1027.  
  1028.     // compare strings: obj constructor.name to type.name.
  1029.     // This is not perfect, we should compare type.prototype to object.__proto__, but mostly code does not change the constructor object.
  1030.     do
  1031.     {
  1032.         if (obj.constructor && obj.constructor.name == type.name)  // then the function that constructed us is the argument
  1033.             return true;
  1034.     }
  1035.     while(obj = obj.__proto__);  // walk the prototype chain.
  1036.     return false;
  1037.     // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Property_Inheritance_Revisited/Determining_Instance_Relationships
  1038. }
  1039.  
  1040. // ************************************************************************************************
  1041. // DOM Modification
  1042.  
  1043. this.setOuterHTML = function(element, html)
  1044. {
  1045.     var doc = element.ownerDocument;
  1046.     var range = doc.createRange();
  1047.     range.selectNode(element || doc.documentElement);
  1048.     try
  1049.     {
  1050.         var fragment = range.createContextualFragment(html);
  1051.         var first = fragment.firstChild;
  1052.         var last = fragment.lastChild;
  1053.         element.parentNode.replaceChild(fragment, element);
  1054.         return [first, last];
  1055.     } catch (e)
  1056.     {
  1057.         return [element,element]
  1058.     }
  1059. };
  1060.  
  1061. this.appendInnerHTML = function(element, html, referenceElement)
  1062. {
  1063.     var doc = element.ownerDocument;
  1064.     var range = doc.createRange();  // a helper object
  1065.     range.selectNodeContents(element); // the environment to interpret the html
  1066.  
  1067.     var fragment = range.createContextualFragment(html);  // parse
  1068.     var firstChild = fragment.firstChild;
  1069.     element.insertBefore(fragment, referenceElement);
  1070.     return firstChild;
  1071. };
  1072.  
  1073. this.insertTextIntoElement = function(element, text)
  1074. {
  1075.     var command = "cmd_insertText";
  1076.  
  1077.     var controller = element.controllers.getControllerForCommand(command);
  1078.     if (!controller || !controller.isCommandEnabled(command))
  1079.         return;
  1080.  
  1081.     var params = this.CCIN("@mozilla.org/embedcomp/command-params;1", "nsICommandParams");
  1082.     params.setStringValue("state_data", text);
  1083.  
  1084.     controller = this.QI(controller, Ci.nsICommandController);
  1085.     controller.doCommandWithParams(command, params);
  1086. };
  1087.  
  1088.  
  1089. // ************************************************************************************************
  1090. // XPath
  1091.  
  1092. /**
  1093.  * Gets an XPath for an element which describes its hierarchical location.
  1094.  */
  1095. this.getElementXPath = function(element)
  1096. {
  1097.     if (element && element.id)
  1098.         return '//*[@id="' + element.id + '"]';
  1099.     else
  1100.         return this.getElementTreeXPath(element);
  1101. };
  1102.  
  1103. this.getElementTreeXPath = function(element)
  1104. {
  1105.     var paths = [];
  1106.  
  1107.     for (; element && element.nodeType == 1; element = element.parentNode)
  1108.     {
  1109.         var index = 0;
  1110.         for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling)
  1111.         {
  1112.             if (sibling.localName == element.localName)
  1113.                 ++index;
  1114.         }
  1115.  
  1116.         var tagName = element.localName.toLowerCase();
  1117.         var pathIndex = (index ? "[" + (index+1) + "]" : "");
  1118.         paths.splice(0, 0, tagName + pathIndex);
  1119.     }
  1120.  
  1121.     return paths.length ? "/" + paths.join("/") : null;
  1122. };
  1123.  
  1124. this.cssToXPath = function(rule)
  1125. {
  1126.     var regElement = /^([#.]?)([a-z0-9\\*_-]*)((\|)([a-z0-9\\*_-]*))?/i;
  1127.     var regAttr1 = /^\[([^\]]*)\]/i;
  1128.     var regAttr2 = /^\[\s*([^~=\s]+)\s*(~?=)\s*"([^"]+)"\s*\]/i;
  1129.     var regPseudo = /^:([a-z_-])+/i;
  1130.     var regCombinator = /^(\s*[>+\s])?/i;
  1131.     var regComma = /^\s*,/i;
  1132.  
  1133.     var index = 1;
  1134.     var parts = ["//", "*"];
  1135.     var lastRule = null;
  1136.  
  1137.     while (rule.length && rule != lastRule)
  1138.     {
  1139.         lastRule = rule;
  1140.  
  1141.         // Trim leading whitespace
  1142.         rule = this.trim(rule);
  1143.         if (!rule.length)
  1144.             break;
  1145.  
  1146.         // Match the element identifier
  1147.         var m = regElement.exec(rule);
  1148.         if (m)
  1149.         {
  1150.             if (!m[1])
  1151.             {
  1152.                 // XXXjoe Namespace ignored for now
  1153.                 if (m[5])
  1154.                     parts[index] = m[5];
  1155.                 else
  1156.                     parts[index] = m[2];
  1157.             }
  1158.             else if (m[1] == '#')
  1159.                 parts.push("[@id='" + m[2] + "']");
  1160.             else if (m[1] == '.')
  1161.                 parts.push("[contains(@class, '" + m[2] + "')]");
  1162.  
  1163.             rule = rule.substr(m[0].length);
  1164.         }
  1165.  
  1166.         // Match attribute selectors
  1167.         m = regAttr2.exec(rule);
  1168.         if (m)
  1169.         {
  1170.             if (m[2] == "~=")
  1171.                 parts.push("[contains(@" + m[1] + ", '" + m[3] + "')]");
  1172.             else
  1173.                 parts.push("[@" + m[1] + "='" + m[3] + "']");
  1174.  
  1175.             rule = rule.substr(m[0].length);
  1176.         }
  1177.         else
  1178.         {
  1179.             m = regAttr1.exec(rule);
  1180.             if (m)
  1181.             {
  1182.                 parts.push("[@" + m[1] + "]");
  1183.                 rule = rule.substr(m[0].length);
  1184.             }
  1185.         }
  1186.  
  1187.         // Skip over pseudo-classes and pseudo-elements, which are of no use to us
  1188.         m = regPseudo.exec(rule);
  1189.         while (m)
  1190.         {
  1191.             rule = rule.substr(m[0].length);
  1192.             m = regPseudo.exec(rule);
  1193.         }
  1194.  
  1195.         // Match combinators
  1196.         m = regCombinator.exec(rule);
  1197.         if (m && m[0].length)
  1198.         {
  1199.             if (m[0].indexOf(">") != -1)
  1200.                 parts.push("/");
  1201.             else if (m[0].indexOf("+") != -1)
  1202.                 parts.push("/following-sibling::");
  1203.             else
  1204.                 parts.push("//");
  1205.  
  1206.             index = parts.length;
  1207.             parts.push("*");
  1208.             rule = rule.substr(m[0].length);
  1209.         }
  1210.  
  1211.         m = regComma.exec(rule);
  1212.         if (m)
  1213.         {
  1214.             parts.push(" | ", "//", "*");
  1215.             index = parts.length-1;
  1216.             rule = rule.substr(m[0].length);
  1217.         }
  1218.     }
  1219.  
  1220.     var xpath = parts.join("");
  1221.     return xpath;
  1222. };
  1223.  
  1224. this.getElementsBySelector = function(doc, css)
  1225. {
  1226.     var xpath = this.cssToXPath(css);
  1227.     return this.getElementsByXPath(doc, xpath);
  1228. };
  1229.  
  1230. this.getElementsByXPath = function(doc, xpath)
  1231. {
  1232.     var nodes = [];
  1233.  
  1234.     try {
  1235.         var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
  1236.         for (var item = result.iterateNext(); item; item = result.iterateNext())
  1237.             nodes.push(item);
  1238.     }
  1239.     catch (exc)
  1240.     {
  1241.         // Invalid xpath expressions make their way here sometimes.  If that happens,
  1242.         // we still want to return an empty set without an exception.
  1243.     }
  1244.  
  1245.     return nodes;
  1246. };
  1247.  
  1248. this.getRuleMatchingElements = function(rule, doc)
  1249. {
  1250.     var css = rule.selectorText;
  1251.     var xpath = this.cssToXPath(css);
  1252.     return this.getElementsByXPath(doc, xpath);
  1253. };
  1254.  
  1255. // ************************************************************************************************
  1256. // Clipboard
  1257.  
  1258. this.copyToClipboard = function(string)
  1259. {
  1260.     var clipboard = this.CCSV("@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
  1261.     clipboard.copyString(string);
  1262. };
  1263.  
  1264. // ************************************************************************************************
  1265. // Graphics
  1266.  
  1267. this.getClientOffset = function(elt)
  1268. {
  1269.     function addOffset(elt, coords, view)
  1270.     {
  1271.         var p = elt.offsetParent;
  1272.  
  1273.         var style = view.getComputedStyle(elt, "");
  1274.  
  1275.         if (elt.offsetLeft)
  1276.             coords.x += elt.offsetLeft + parseInt(style.borderLeftWidth);
  1277.         if (elt.offsetTop)
  1278.             coords.y += elt.offsetTop + parseInt(style.borderTopWidth);
  1279.  
  1280.         if (p)
  1281.         {
  1282.             if (p.nodeType == 1)
  1283.                 addOffset(p, coords, view);
  1284.         }
  1285.         else if (elt.ownerDocument.defaultView.frameElement)
  1286.             addOffset(elt.ownerDocument.defaultView.frameElement, coords, elt.ownerDocument.defaultView);
  1287.     }
  1288.  
  1289.     var coords = {x: 0, y: 0};
  1290.     if (elt)
  1291.     {
  1292.         var view = elt.ownerDocument.defaultView;
  1293.         addOffset(elt, coords, view);
  1294.     }
  1295.  
  1296.     return coords;
  1297. };
  1298.  
  1299. this.getLTRBWH = function(elt)
  1300. {
  1301.     var bcrect,
  1302.         dims = {"left": 0, "top": 0, "right": 0, "bottom": 0, "width": 0, "height": 0};
  1303.  
  1304.     if (elt)
  1305.     {
  1306.         bcrect = elt.getBoundingClientRect();
  1307.         dims.left = bcrect.left;
  1308.         dims.top = bcrect.top;
  1309.         dims.right = bcrect.right;
  1310.         dims.bottom = bcrect.bottom;
  1311.  
  1312.         if(bcrect.width)
  1313.         {
  1314.             dims.width = bcrect.width;
  1315.             dims.height = bcrect.height;
  1316.         }
  1317.         else
  1318.         {
  1319.             dims.width = dims.right - dims.left;
  1320.             dims.height = dims.bottom - dims.top;
  1321.         }
  1322.     }
  1323.     return dims;
  1324. };
  1325.  
  1326. this.applyBodyOffsets = function(elt, clientRect)
  1327. {
  1328.     var od = elt.ownerDocument;
  1329.     if (!od.body)
  1330.         return clientRect;
  1331.  
  1332.     var style = od.defaultView.getComputedStyle(od.body, null);
  1333.  
  1334.     var pos = style.getPropertyValue('position');
  1335.     if(pos === 'absolute' || pos === 'relative')
  1336.     {
  1337.         var borderLeft = parseInt(style.getPropertyValue('border-left-width').replace('px', ''),10) || 0;
  1338.         var borderTop = parseInt(style.getPropertyValue('border-top-width').replace('px', ''),10) || 0;
  1339.         var paddingLeft = parseInt(style.getPropertyValue('padding-left').replace('px', ''),10) || 0;
  1340.         var paddingTop = parseInt(style.getPropertyValue('padding-top').replace('px', ''),10) || 0;
  1341.         var marginLeft = parseInt(style.getPropertyValue('margin-left').replace('px', ''),10) || 0;
  1342.         var marginTop = parseInt(style.getPropertyValue('margin-top').replace('px', ''),10) || 0;
  1343.  
  1344.         var offsetX = borderLeft + paddingLeft + marginLeft;
  1345.         var offsetY = borderTop + paddingTop + marginTop;
  1346.  
  1347.         clientRect.left -= offsetX;
  1348.         clientRect.top -= offsetY;
  1349.         clientRect.right -= offsetX;
  1350.         clientRect.bottom -= offsetY;
  1351.     }
  1352.  
  1353.     return clientRect;
  1354. };
  1355.  
  1356. this.getOffsetSize = function(elt)
  1357. {
  1358.     return {width: elt.offsetWidth, height: elt.offsetHeight};
  1359. };
  1360.  
  1361. this.getOverflowParent = function(element)
  1362. {
  1363.     for (var scrollParent = element.parentNode; scrollParent; scrollParent = scrollParent.offsetParent)
  1364.     {
  1365.         if (scrollParent.scrollHeight > scrollParent.offsetHeight)
  1366.             return scrollParent;
  1367.     }
  1368. };
  1369.  
  1370. this.isScrolledToBottom = function(element)
  1371. {
  1372.     var onBottom = (element.scrollTop + element.offsetHeight) == element.scrollHeight;
  1373.     return onBottom;
  1374. };
  1375.  
  1376. this.scrollToBottom = function(element)
  1377. {
  1378.     element.scrollTop = element.scrollHeight;
  1379.  
  1380.     return (element.scrollTop == element.scrollHeight);
  1381. };
  1382.  
  1383. this.move = function(element, x, y)
  1384. {
  1385.     element.style.left = x + "px";
  1386.     element.style.top = y + "px";
  1387. };
  1388.  
  1389. this.resize = function(element, w, h)
  1390. {
  1391.     element.style.width = w + "px";
  1392.     element.style.height = h + "px";
  1393. };
  1394.  
  1395. this.linesIntoCenterView = function(element, scrollBox)  // {before: int, after: int}
  1396. {
  1397.     if (!scrollBox)
  1398.         scrollBox = this.getOverflowParent(element);
  1399.  
  1400.     if (!scrollBox)
  1401.         return;
  1402.  
  1403.     var offset = this.getClientOffset(element);
  1404.  
  1405.     var topSpace = offset.y - scrollBox.scrollTop;
  1406.     var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
  1407.             - (offset.y + element.offsetHeight);
  1408.  
  1409.     if (topSpace < 0 || bottomSpace < 0)
  1410.     {
  1411.         var split = (scrollBox.clientHeight/2);
  1412.         var centerY = offset.y - split;
  1413.         scrollBox.scrollTop = centerY;
  1414.         topSpace = split;
  1415.         bottomSpace = split -  element.offsetHeight;
  1416.     }
  1417.  
  1418.     return {before: Math.round((topSpace/element.offsetHeight) + 0.5),
  1419.             after: Math.round((bottomSpace/element.offsetHeight) + 0.5) }
  1420. };
  1421.  
  1422. this.scrollIntoCenterView = function(element, scrollBox, notX, notY)
  1423. {
  1424.     if (!element)
  1425.         return;
  1426.  
  1427.     if (!scrollBox)
  1428.         scrollBox = this.getOverflowParent(element);
  1429.  
  1430.     if (!scrollBox)
  1431.         return;
  1432.  
  1433.     var offset = this.getClientOffset(element);
  1434.  
  1435.     if (!notY)
  1436.     {
  1437.         var topSpace = offset.y - scrollBox.scrollTop;
  1438.         var bottomSpace = (scrollBox.scrollTop + scrollBox.clientHeight)
  1439.             - (offset.y + element.offsetHeight);
  1440.  
  1441.         if (topSpace < 0 || bottomSpace < 0)
  1442.         {
  1443.             var centerY = offset.y - (scrollBox.clientHeight/2);
  1444.             scrollBox.scrollTop = centerY;
  1445.         }
  1446.     }
  1447.  
  1448.     if (!notX)
  1449.     {
  1450.         var leftSpace = offset.x - scrollBox.scrollLeft;
  1451.         var rightSpace = (scrollBox.scrollLeft + scrollBox.clientWidth)
  1452.             - (offset.x + element.clientWidth);
  1453.  
  1454.         if (leftSpace < 0 || rightSpace < 0)
  1455.         {
  1456.             var centerX = offset.x - (scrollBox.clientWidth/2);
  1457.             scrollBox.scrollLeft = centerX;
  1458.         }
  1459.     }
  1460. };
  1461.  
  1462. // ************************************************************************************************
  1463. // CSS
  1464.  
  1465. var cssKeywordMap = null;
  1466. var cssPropNames = null;
  1467. var cssColorNames = null;
  1468. var imageRules = null;
  1469.  
  1470. this.getCSSKeywordsByProperty = function(propName)
  1471. {
  1472.     if (!cssKeywordMap)
  1473.     {
  1474.         cssKeywordMap = {};
  1475.  
  1476.         for (var name in this.cssInfo)
  1477.         {
  1478.             var list = [];
  1479.  
  1480.             var types = this.cssInfo[name];
  1481.             for (var i = 0; i < types.length; ++i)
  1482.             {
  1483.                 var keywords = this.cssKeywords[types[i]];
  1484.                 if (keywords)
  1485.                     list.push.apply(list, keywords);
  1486.             }
  1487.  
  1488.             cssKeywordMap[name] = list;
  1489.         }
  1490.     }
  1491.  
  1492.     return propName in cssKeywordMap ? cssKeywordMap[propName] : [];
  1493. };
  1494.  
  1495. this.getCSSPropertyNames = function()
  1496. {
  1497.     if (!cssPropNames)
  1498.     {
  1499.         cssPropNames = [];
  1500.  
  1501.         for (var name in this.cssInfo)
  1502.             cssPropNames.push(name);
  1503.     }
  1504.  
  1505.     return cssPropNames;
  1506. };
  1507.  
  1508. this.isColorKeyword = function(keyword)
  1509. {
  1510.     if (keyword == "transparent")
  1511.         return false;
  1512.  
  1513.     if (!cssColorNames)
  1514.     {
  1515.         cssColorNames = [];
  1516.  
  1517.         var colors = this.cssKeywords["color"];
  1518.         for (var i = 0; i < colors.length; ++i)
  1519.             cssColorNames.push(colors[i].toLowerCase());
  1520.  
  1521.         var systemColors = this.cssKeywords["systemColor"];
  1522.         for (var i = 0; i < systemColors.length; ++i)
  1523.             cssColorNames.push(systemColors[i].toLowerCase());
  1524.     }
  1525.  
  1526.     return cssColorNames.indexOf(keyword.toLowerCase()) != -1;
  1527. };
  1528.  
  1529. this.isImageRule = function(rule)
  1530. {
  1531.     if (!imageRules)
  1532.     {
  1533.         imageRules = [];
  1534.  
  1535.         for (var i in this.cssInfo)
  1536.         {
  1537.             var r = i.toLowerCase();
  1538.             var suffix = "image";
  1539.             if (r.match(suffix + "$") == suffix || r == "background")
  1540.                 imageRules.push(r);
  1541.         }
  1542.     }
  1543.  
  1544.     return imageRules.indexOf(rule.toLowerCase()) != -1;
  1545. };
  1546.  
  1547. this.copyTextStyles = function(fromNode, toNode, style)
  1548. {
  1549.     var view = fromNode.ownerDocument.defaultView;
  1550.     if (view)
  1551.     {
  1552.         if (!style)
  1553.             style = view.getComputedStyle(fromNode, "");
  1554.  
  1555.         toNode.style.fontFamily = style.getPropertyCSSValue("font-family").cssText;
  1556.         toNode.style.fontSize = style.getPropertyCSSValue("font-size").cssText;
  1557.         toNode.style.fontWeight = style.getPropertyCSSValue("font-weight").cssText;
  1558.         toNode.style.fontStyle = style.getPropertyCSSValue("font-style").cssText;
  1559.  
  1560.         return style;
  1561.     }
  1562. };
  1563.  
  1564. this.copyBoxStyles = function(fromNode, toNode, style)
  1565. {
  1566.     var view = fromNode.ownerDocument.defaultView;
  1567.     if (view)
  1568.     {
  1569.         if (!style)
  1570.             style = view.getComputedStyle(fromNode, "");
  1571.  
  1572.         toNode.style.marginTop = style.getPropertyCSSValue("margin-top").cssText;
  1573.         toNode.style.marginRight = style.getPropertyCSSValue("margin-right").cssText;
  1574.         toNode.style.marginBottom = style.getPropertyCSSValue("margin-bottom").cssText;
  1575.         toNode.style.marginLeft = style.getPropertyCSSValue("margin-left").cssText;
  1576.         toNode.style.borderTopWidth = style.getPropertyCSSValue("border-top-width").cssText;
  1577.         toNode.style.borderRightWidth = style.getPropertyCSSValue("border-right-width").cssText;
  1578.         toNode.style.borderBottomWidth = style.getPropertyCSSValue("border-bottom-width").cssText;
  1579.         toNode.style.borderLeftWidth = style.getPropertyCSSValue("border-left-width").cssText;
  1580.  
  1581.         return style;
  1582.     }
  1583. };
  1584.  
  1585. this.readBoxStyles = function(style)
  1586. {
  1587.     const styleNames = {
  1588.         "margin-top": "marginTop", "margin-right": "marginRight",
  1589.         "margin-left": "marginLeft", "margin-bottom": "marginBottom",
  1590.         "border-top-width": "borderTop", "border-right-width": "borderRight",
  1591.         "border-left-width": "borderLeft", "border-bottom-width": "borderBottom",
  1592.         "padding-top": "paddingTop", "padding-right": "paddingRight",
  1593.         "padding-left": "paddingLeft", "padding-bottom": "paddingBottom",
  1594.         "z-index": "zIndex",
  1595.     };
  1596.  
  1597.     var styles = {};
  1598.     for (var styleName in styleNames)
  1599.         styles[styleNames[styleName]] = parseInt(style.getPropertyCSSValue(styleName).cssText) || 0;
  1600.     return styles;
  1601. };
  1602.  
  1603. this.getBoxFromStyles = function(style, element)
  1604. {
  1605.     var args = this.readBoxStyles(style);
  1606.     args.width = element.offsetWidth
  1607.         - (args.paddingLeft+args.paddingRight+args.borderLeft+args.borderRight);
  1608.     args.height = element.offsetHeight
  1609.         - (args.paddingTop+args.paddingBottom+args.borderTop+args.borderBottom);
  1610.     return args;
  1611. };
  1612.  
  1613. this.getElementCSSSelector = function(element)
  1614. {
  1615.     var label = element.localName.toLowerCase();
  1616.     if (element.id)
  1617.         label += "#" + element.id;
  1618.     if (element.hasAttribute("class"))
  1619.         label += "." + element.getAttribute("class").split(" ")[0];
  1620.  
  1621.     return label;
  1622. };
  1623.  
  1624. this.getURLForStyleSheet= function(styleSheet)
  1625. {
  1626.     //http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html#StyleSheets-StyleSheet. For inline style sheets, the value of this attribute is null.
  1627.     return (styleSheet.href ? styleSheet.href : styleSheet.ownerNode.ownerDocument.URL);
  1628. };
  1629.  
  1630. this.getDocumentForStyleSheet = function(styleSheet)
  1631. {
  1632.     while (styleSheet.parentStyleSheet && !styleSheet.ownerNode)
  1633.     {
  1634.         styleSheet = styleSheet.parentStyleSheet;
  1635.     }
  1636.     if (styleSheet.ownerNode)
  1637.       return styleSheet.ownerNode.ownerDocument;
  1638. };
  1639.  
  1640. /**
  1641.  * Retrieves the instance number for a given style sheet. The instance number
  1642.  * is sheet's index within the set of all other sheets whose URL is the same.
  1643.  */
  1644. this.getInstanceForStyleSheet = function(styleSheet, ownerDocument)
  1645. {
  1646.     // System URLs are always unique (or at least we are making this assumption)
  1647.     if (FBL.isSystemStyleSheet(styleSheet))
  1648.         return 0;
  1649.  
  1650.     // ownerDocument is an optional hint for performance
  1651.     ownerDocument = ownerDocument || FBL.getDocumentForStyleSheet(styleSheet);
  1652.  
  1653.     var ret = 0,
  1654.         styleSheets = ownerDocument.styleSheets,
  1655.         href = styleSheet.href;
  1656.     for (var i = 0; i < styleSheets.length; i++)
  1657.     {
  1658.         var curSheet = styleSheets[i];
  1659.         if (curSheet == styleSheet)
  1660.             break;
  1661.         if (curSheet.href == href)
  1662.             ret++;
  1663.     }
  1664.     return ret;
  1665. };
  1666.  
  1667. // ************************************************************************************************
  1668. // HTML and XML Serialization
  1669.  
  1670.  
  1671. var getElementType = this.getElementType = function(node)
  1672. {
  1673.     if (isElementXUL(node))
  1674.         return 'xul';
  1675.     else if (isElementSVG(node))
  1676.         return 'svg';
  1677.     else if (isElementMathML(node))
  1678.         return 'mathml';
  1679.     else if (isElementXHTML(node))
  1680.         return 'xhtml';
  1681.     else if (isElementHTML(node))
  1682.         return 'html';
  1683. }
  1684.  
  1685. var isElementHTML = this.isElementHTML = function(node)
  1686. {
  1687.     return node.nodeName == node.nodeName.toUpperCase();
  1688. }
  1689.  
  1690. var isElementXHTML = this.isElementXHTML = function(node)
  1691. {
  1692.     return node.nodeName == node.nodeName.toLowerCase();
  1693. }
  1694.  
  1695. var isElementMathML = this.isElementMathML = function(node)
  1696. {
  1697.     return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
  1698. }
  1699.  
  1700. var isElementSVG = this.isElementSVG = function(node)
  1701. {
  1702.     return node.namespaceURI == 'http://www.w3.org/2000/svg';
  1703. }
  1704.  
  1705. var isElementXUL = this.isElementXUL = function(node)
  1706. {
  1707.     return node instanceof XULElement;
  1708. }
  1709.  
  1710. this.isSelfClosing = function(element)
  1711. {
  1712.     if (isElementSVG(element) || isElementMathML(element))
  1713.         return true;
  1714.     var tag = element.localName.toLowerCase();
  1715.     return (this.selfClosingTags.hasOwnProperty(tag));
  1716. };
  1717.  
  1718. this.getElementHTML = function(element)
  1719. {
  1720.     var self=this;
  1721.     function toHTML(elt)
  1722.     {
  1723.         if (elt.nodeType == Node.ELEMENT_NODE)
  1724.         {
  1725.             if (unwrapObject(elt).firebugIgnore)
  1726.                 return;
  1727.  
  1728.             html.push('<', elt.nodeName.toLowerCase());
  1729.  
  1730.             for (var i = 0; i < elt.attributes.length; ++i)
  1731.             {
  1732.                 var attr = elt.attributes[i];
  1733.  
  1734.                 // Hide attributes set by Firebug
  1735.                 if (attr.localName.indexOf("firebug-") == 0)
  1736.                     continue;
  1737.  
  1738.                 // MathML
  1739.                 if (attr.localName.indexOf("-moz-math") == 0)
  1740.                 {
  1741.                     // just hide for now
  1742.                     continue;
  1743.                 }
  1744.  
  1745.                 html.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
  1746.             }
  1747.  
  1748.             if (elt.firstChild)
  1749.             {
  1750.                 html.push('>');
  1751.  
  1752.                 var pureText=true;
  1753.                 for (var child = element.firstChild; child; child = child.nextSibling)
  1754.                     pureText=pureText && (child.nodeType == Node.TEXT_NODE);
  1755.  
  1756.                 if (pureText)
  1757.                     html.push(escapeForHtmlEditor(elt.textContent));
  1758.                 else {
  1759.                     for (var child = elt.firstChild; child; child = child.nextSibling)
  1760.                         toHTML(child);
  1761.                 }
  1762.  
  1763.                 html.push('</', elt.nodeName.toLowerCase(), '>');
  1764.             }
  1765.             else if (isElementSVG(elt) || isElementMathML(elt))
  1766.             {
  1767.                 html.push('/>');
  1768.             }
  1769.             else if (self.isSelfClosing(elt))
  1770.             {
  1771.                 html.push((isElementXHTML(elt))?'/>':'>');
  1772.             }
  1773.             else
  1774.             {
  1775.                 html.push('></', elt.nodeName.toLowerCase(), '>');
  1776.             }
  1777.         }
  1778.         else if (elt.nodeType == Node.TEXT_NODE)
  1779.             html.push(escapeForTextNode(elt.textContent));
  1780.         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
  1781.             html.push('<![CDATA[', elt.nodeValue, ']]>');
  1782.         else if (elt.nodeType == Node.COMMENT_NODE)
  1783.             html.push('<!--', elt.nodeValue, '-->');
  1784.     }
  1785.  
  1786.     var html = [];
  1787.     toHTML(element);
  1788.     return html.join("");
  1789. };
  1790.  
  1791. this.getElementXML = function(element)
  1792. {
  1793.     function toXML(elt)
  1794.     {
  1795.         if (elt.nodeType == Node.ELEMENT_NODE)
  1796.         {
  1797.             if (unwrapObject(elt).firebugIgnore)
  1798.                 return;
  1799.  
  1800.             xml.push('<', elt.nodeName.toLowerCase());
  1801.  
  1802.             for (var i = 0; i < elt.attributes.length; ++i)
  1803.             {
  1804.                 var attr = elt.attributes[i];
  1805.  
  1806.                 // Hide attributes set by Firebug
  1807.                 if (attr.localName.indexOf("firebug-") == 0)
  1808.                     continue;
  1809.  
  1810.                 // MathML
  1811.                 if (attr.localName.indexOf("-moz-math") == 0)
  1812.                 {
  1813.                     // just hide for now
  1814.                     continue;
  1815.                 }
  1816.  
  1817.                 xml.push(' ', attr.nodeName, '="', escapeForElementAttribute(attr.nodeValue),'"');
  1818.             }
  1819.  
  1820.             if (elt.firstChild)
  1821.             {
  1822.                 xml.push('>');
  1823.  
  1824.                 for (var child = elt.firstChild; child; child = child.nextSibling)
  1825.                     toXML(child);
  1826.  
  1827.                 xml.push('</', elt.nodeName.toLowerCase(), '>');
  1828.             }
  1829.             else
  1830.                 xml.push('/>');
  1831.         }
  1832.         else if (elt.nodeType == Node.TEXT_NODE)
  1833.             xml.push(elt.nodeValue);
  1834.         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
  1835.             xml.push('<![CDATA[', elt.nodeValue, ']]>');
  1836.         else if (elt.nodeType == Node.COMMENT_NODE)
  1837.             xml.push('<!--', elt.nodeValue, '-->');
  1838.     }
  1839.  
  1840.     var xml = [];
  1841.     toXML(element);
  1842.     return xml.join("");
  1843. };
  1844.  
  1845. // ************************************************************************************************
  1846. // Whitespace and Entity conversions
  1847.  
  1848. var entityConversionLists = this.entityConversionLists = {
  1849.     normal : {
  1850.         whitespace : {
  1851.             '\t' : '\u200c\u2192',
  1852.             '\n' : '\u200c\u00b6',
  1853.             '\r' : '\u200c\u00ac',
  1854.             ' '  : '\u200c\u00b7'
  1855.         }
  1856.     },
  1857.     reverse : {
  1858.         whitespace : {
  1859.             ' ' : '\t',
  1860.             ' ' : '\n',
  1861.             '\u200c\u2192' : '\t',
  1862.             '\u200c\u00b6' : '\n',
  1863.             '\u200c\u00ac' : '\r',
  1864.             '\u200c\u00b7' : ' '
  1865.         }
  1866.     }
  1867. };
  1868.  
  1869. var normal = entityConversionLists.normal,
  1870.     reverse = entityConversionLists.reverse;
  1871.  
  1872. function addEntityMapToList(ccode, entity)
  1873. {
  1874.     var lists = Array.slice(arguments, 2),
  1875.         len = lists.length,
  1876.         ch = String.fromCharCode(ccode);
  1877.     for (var i = 0; i < len; i++)
  1878.     {
  1879.         var list = lists[i];
  1880.         normal[list]=normal[list] || {};
  1881.         normal[list][ch] = '&' + entity + ';';
  1882.         reverse[list]=reverse[list] || {};
  1883.         reverse[list]['&' + entity + ';'] = ch;
  1884.     }
  1885. }
  1886.  
  1887. var e = addEntityMapToList,
  1888.     white = 'whitespace',
  1889.     text = 'text',
  1890.     attr = 'attributes',
  1891.     css = 'css',
  1892.     editor = 'editor';
  1893.  
  1894. e(0x0022, 'quot', attr, css);
  1895. e(0x0026, 'amp', attr, text, css);
  1896. e(0x0027, 'apos', css);
  1897. e(0x003c, 'lt', attr, text, css);
  1898. e(0x003e, 'gt', attr, text, css);
  1899. e(0xa9, 'copy', text, editor);
  1900. e(0xae, 'reg', text, editor);
  1901. e(0x2122, 'trade', text, editor);
  1902.  
  1903. // See http://en.wikipedia.org/wiki/Dash
  1904. e(0x2012, '#8210', attr, text, editor); // figure dash
  1905. e(0x2013, 'ndash', attr, text, editor); // en dash
  1906. e(0x2014, 'mdash', attr, text, editor); // em dash
  1907. e(0x2015, '#8213', attr, text, editor); // horizontal bar
  1908.  
  1909. e(0x00a0, 'nbsp', attr, text, white, editor);
  1910. e(0x2002, 'ensp', attr, text, white, editor);
  1911. e(0x2003, 'emsp', attr, text, white, editor);
  1912. e(0x2009, 'thinsp', attr, text, white, editor);
  1913. e(0x200c, 'zwnj', attr, text, white, editor);
  1914. e(0x200d, 'zwj', attr, text, white, editor);
  1915. e(0x200e, 'lrm', attr, text, white, editor);
  1916. e(0x200f, 'rlm', attr, text, white, editor);
  1917. e(0x200b, '#8203', attr, text, white, editor); // zero-width space (ZWSP)
  1918.  
  1919. //************************************************************************************************
  1920. // Entity escaping
  1921.  
  1922. var entityConversionRegexes = {
  1923.         normal : {},
  1924.         reverse : {}
  1925.     };
  1926.  
  1927. var escapeEntitiesRegEx = {
  1928.     normal : function(list)
  1929.     {
  1930.         var chars = [];
  1931.         for ( var ch in list)
  1932.         {
  1933.             chars.push(ch);
  1934.         }
  1935.         return new RegExp('([' + chars.join('') + '])', 'gm');
  1936.     },
  1937.     reverse : function(list)
  1938.     {
  1939.         var chars = [];
  1940.         for ( var ch in list)
  1941.         {
  1942.             chars.push(ch);
  1943.         }
  1944.         return new RegExp('(' + chars.join('|') + ')', 'gm');
  1945.     }
  1946. };
  1947.  
  1948. function getEscapeRegexp(direction, lists)
  1949. {
  1950.     var name = '', re;
  1951.     var groups = [].concat(lists);
  1952.     for (i = 0; i < groups.length; i++)
  1953.     {
  1954.         name += groups[i].group;
  1955.     }
  1956.     re = entityConversionRegexes[direction][name];
  1957.     if (!re)
  1958.     {
  1959.         var list = {};
  1960.         if (groups.length > 1)
  1961.         {
  1962.             for ( var i = 0; i < groups.length; i++)
  1963.             {
  1964.                 var aList = entityConversionLists[direction][groups[i].group];
  1965.                 for ( var item in aList)
  1966.                     list[item] = aList[item];
  1967.             }
  1968.         } else if (groups.length==1)
  1969.         {
  1970.             list = entityConversionLists[direction][groups[0].group]; // faster for special case
  1971.         } else {
  1972.             list = {}; // perhaps should print out an error here?
  1973.         }
  1974.         re = entityConversionRegexes[direction][name] = escapeEntitiesRegEx[direction](list);
  1975.     }
  1976.     return re;
  1977. }
  1978.  
  1979. function createSimpleEscape(name, direction)
  1980. {
  1981.     return function(value)
  1982.     {
  1983.         var list = entityConversionLists[direction][name];
  1984.         return String(value).replace(
  1985.                 getEscapeRegexp(direction, {
  1986.                     group : name,
  1987.                     list : list
  1988.                 }),
  1989.                 function(ch)
  1990.                 {
  1991.                     return list[ch];
  1992.                 }
  1993.                );
  1994.     }
  1995. }
  1996.  
  1997. function escapeGroupsForEntities(str, lists)
  1998. {
  1999.     lists = [].concat(lists);
  2000.     var re = getEscapeRegexp('normal', lists),
  2001.         split = String(str).split(re),
  2002.         len = split.length,
  2003.         results = [],
  2004.         cur, r, i, ri = 0, l, list, last = '';
  2005.     if (!len)
  2006.         return [ {
  2007.             str : String(str),
  2008.             group : '',
  2009.             name : ''
  2010.         } ];
  2011.     for (i = 0; i < len; i++)
  2012.     {
  2013.         cur = split[i];
  2014.         if (cur == '')
  2015.             continue;
  2016.         for (l = 0; l < lists.length; l++)
  2017.         {
  2018.             list = lists[l];
  2019.             r = entityConversionLists.normal[list.group][cur];
  2020.             // if (cur == ' ' && list.group == 'whitespace' && last == ' ') // only show for runs of more than one space
  2021.             //     r = ' ';
  2022.             if (r)
  2023.             {
  2024.                 results[ri] = {
  2025.                     'str' : r,
  2026.                     'class' : list['class'],
  2027.                     'extra' : list.extra[cur] ? list['class']
  2028.                             + list.extra[cur] : ''
  2029.                 };
  2030.                 break;
  2031.             }
  2032.         }
  2033.         // last=cur;
  2034.         if (!r)
  2035.             results[ri] = {
  2036.                 'str' : cur,
  2037.                 'class' : '',
  2038.                 'extra' : ''
  2039.             };
  2040.         ri++;
  2041.     }
  2042.     return results;
  2043. }
  2044.  
  2045. this.escapeGroupsForEntities = escapeGroupsForEntities;
  2046.  
  2047.  
  2048. function unescapeEntities(str, lists)
  2049. {
  2050.     var re = getEscapeRegexp('reverse', lists),
  2051.         split = String(str).split(re),
  2052.         len = split.length,
  2053.         results = [],
  2054.         cur, r, i, ri = 0, l, list;
  2055.     if (!len)
  2056.         return str;
  2057.     lists = [].concat(lists);
  2058.     for (i = 0; i < len; i++)
  2059.     {
  2060.         cur = split[i];
  2061.         if (cur == '')
  2062.             continue;
  2063.         for (l = 0; l < lists.length; l++)
  2064.         {
  2065.             list = lists[l];
  2066.             r = entityConversionLists.reverse[list.group][cur];
  2067.             if (r)
  2068.             {
  2069.                 results[ri] = r;
  2070.                 break;
  2071.             }
  2072.         }
  2073.         if (!r)
  2074.             results[ri] = cur;
  2075.         ri++;
  2076.     }
  2077.     return results.join('') || '';
  2078. }
  2079.  
  2080.  
  2081. // ************************************************************************************************
  2082. // String escaping
  2083.  
  2084. var escapeForTextNode = this.escapeForTextNode = createSimpleEscape('text', 'normal');
  2085. var escapeForHtmlEditor = this.escapeForHtmlEditor = createSimpleEscape('editor', 'normal');
  2086. var escapeForElementAttribute = this.escapeForElementAttribute = createSimpleEscape('attributes', 'normal');
  2087. var escapeForCss = this.escapeForCss = createSimpleEscape('css', 'normal');
  2088.  
  2089. // deprecated compatibility functions
  2090. this.deprecateEscapeHTML = createSimpleEscape('text', 'normal');
  2091. this.deprecatedUnescapeHTML = createSimpleEscape('text', 'reverse');
  2092. this.escapeHTML = deprecated("use appropriate escapeFor... function", this.deprecateEscapeHTML);
  2093. this.unescapeHTML = deprecated("use appropriate unescapeFor... function", this.deprecatedUnescapeHTML);
  2094.  
  2095. var escapeForSourceLine = this.escapeForSourceLine = createSimpleEscape('text', 'normal');
  2096.  
  2097. var unescapeWhitespace = createSimpleEscape('whitespace', 'reverse');
  2098.  
  2099. this.unescapeForTextNode = function(str)
  2100. {
  2101.     if (Firebug.showTextNodesWithWhitespace)
  2102.         str = unescapeWhitespace(str);
  2103.     if (!Firebug.showTextNodesWithEntities)
  2104.         str = escapeForElementAttribute(str);
  2105.     return str;
  2106. }
  2107.  
  2108. this.escapeNewLines = function(value)
  2109. {
  2110.     return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n");
  2111. };
  2112.  
  2113. this.stripNewLines = function(value)
  2114. {
  2115.     return typeof(value) == "string" ? value.replace(/[\r\n]/gm, " ") : value;
  2116. };
  2117.  
  2118. this.escapeJS = function(value)
  2119. {
  2120.     return value.replace(/\r/gm, "\\r").replace(/\n/gm, "\\n").replace('"', '\\"', "g");
  2121. };
  2122.  
  2123. this.cropString = function(text, limit, alterText)
  2124. {
  2125.     if (!alterText)
  2126.         alterText = "..."; //ΓǪ
  2127.  
  2128.     text = text + "";
  2129.  
  2130.     if (!limit)
  2131.         limit = Firebug.stringCropLength;
  2132.     var halfLimit = (limit / 2);
  2133.     halfLimit -= 2; // adjustment for alterText's increase in size
  2134.  
  2135.     if (text.length > limit)
  2136.         return text.substr(0, halfLimit) + alterText + text.substr(text.length-halfLimit);
  2137.     else
  2138.         return text;
  2139. };
  2140.  
  2141. this.cropMultipleLines = function(text, limit)
  2142. {
  2143.     return this.escapeNewLines(this.cropString(text, limit));
  2144. };
  2145.  
  2146. this.isWhitespace = function(text)
  2147. {
  2148.     return !reNotWhitespace.exec(text);
  2149. };
  2150.  
  2151. this.splitLines = function(text)
  2152. {
  2153.     const reSplitLines2 = /.*(:?\r\n|\n|\r)?/mg;
  2154.     var lines;
  2155.     if (text.match)
  2156.     {
  2157.         lines = text.match(reSplitLines2);
  2158.     }
  2159.     else
  2160.     {
  2161.         var str = text+"";
  2162.         lines = str.match(reSplitLines2);
  2163.     }
  2164.     lines.pop();
  2165.     return lines;
  2166. };
  2167.  
  2168. this.trim = function(text)
  2169. {
  2170.     return text.replace(/^\s*|\s*$/g,"");
  2171. }
  2172.  
  2173. this.trimLeft = function(text)
  2174. {
  2175.     return text.replace(/^\s+/,"");
  2176. }
  2177.  
  2178. this.trimRight = function(text)
  2179. {
  2180.     return text.replace(/\s+$/,"");
  2181. }
  2182.  
  2183. this.wrapText = function(text, noEscapeHTML)
  2184. {
  2185.     var reNonAlphaNumeric = /[^A-Za-z_$0-9'"-]/;
  2186.  
  2187.     var html = [];
  2188.     var wrapWidth = Firebug.textWrapWidth;
  2189.  
  2190.     // Split long text into lines and put every line into an <code> element (only in case
  2191.     // if noEscapeHTML is false). This is useful for automatic scrolling when searching
  2192.     // within response body (in order to scroll we need an element).
  2193.     // Don't use <pre> elements since these adds addiontanl new line ending when copying
  2194.     // selected source code using Firefox->Edit->Copy (Ctrl+C) (issue 2093).
  2195.     var lines = this.splitLines(text);
  2196.     for (var i = 0; i < lines.length; ++i)
  2197.     {
  2198.         var line = lines[i];
  2199.         while (line.length > wrapWidth)
  2200.         {
  2201.             var m = reNonAlphaNumeric.exec(line.substr(wrapWidth, 100));
  2202.             var wrapIndex = wrapWidth + (m ? m.index : 0);
  2203.             var subLine = line.substr(0, wrapIndex);
  2204.             line = line.substr(wrapIndex);
  2205.  
  2206.             if (!noEscapeHTML) html.push("<code class=\"wrappedText focusRow\" role=\"listitem\">");
  2207.             html.push(noEscapeHTML ? subLine : escapeForTextNode(subLine));
  2208.             if (!noEscapeHTML) html.push("</code>");
  2209.         }
  2210.  
  2211.         if (!noEscapeHTML) html.push("<code class=\"wrappedText focusRow\" role=\"listitem\">");
  2212.         html.push(noEscapeHTML ? line : escapeForTextNode(line));
  2213.         if (!noEscapeHTML) html.push("</code>");
  2214.     }
  2215.  
  2216.     return html;
  2217. }
  2218.  
  2219. this.insertWrappedText = function(text, textBox, noEscapeHTML)
  2220. {
  2221.     var html = this.wrapText(text, noEscapeHTML);
  2222.     textBox.innerHTML = "<pre role=\"list\">" + html.join("") + "</pre>";
  2223. }
  2224.  
  2225. // ************************************************************************************************
  2226. // Menus
  2227.  
  2228. this.createMenu = function(popup, label)
  2229. {
  2230.     var menu = popup.ownerDocument.createElement("menu");
  2231.     menu.setAttribute("label", label);
  2232.  
  2233.     var menuPopup = popup.ownerDocument.createElement("menupopup");
  2234.  
  2235.     popup.appendChild(menu);
  2236.     menu.appendChild(menuPopup);
  2237.  
  2238.     return menuPopup;
  2239. };
  2240.  
  2241. this.createMenuItem = function(popup, item, before)
  2242. {
  2243.     if (typeof(item) == "string" && item.indexOf("-") == 0)
  2244.         return this.createMenuSeparator(popup, before);
  2245.  
  2246.     var menuitem = popup.ownerDocument.createElement("menuitem");
  2247.  
  2248.     this.setItemIntoElement(menuitem, item);
  2249.  
  2250.     if (before)
  2251.         popup.insertBefore(menuitem, before);
  2252.     else
  2253.         popup.appendChild(menuitem);
  2254.     return menuitem;
  2255. };
  2256.  
  2257. this.setItemIntoElement = function(element, item)
  2258. {
  2259.     var label = item.nol10n ? item.label : this.$STR(item.label);
  2260.  
  2261.     element.setAttribute("label", label);
  2262.     element.setAttribute("type", item.type);
  2263.     if (item.checked)
  2264.         element.setAttribute("checked", "true");
  2265.     if (item.disabled)
  2266.         element.setAttribute("disabled", "true");
  2267.     if (item.image)
  2268.     {
  2269.         element.setAttribute("class", "element-iconic");
  2270.         element.setAttribute("image", item.image);
  2271.     }
  2272.  
  2273.     if (item.command)
  2274.         element.addEventListener("command", item.command, false);
  2275.  
  2276.     if (item.commandID)
  2277.         element.setAttribute("command", item.commandID);
  2278.  
  2279.     if (item.option)
  2280.         element.setAttribute("option", item.option);
  2281.  
  2282.     if (item.tooltiptext)
  2283.         element.setAttribute("tooltiptext", item.tooltiptext);
  2284.  
  2285.     return element;
  2286. }
  2287.  
  2288.  
  2289. this.createMenuHeader = function(popup, item)
  2290. {
  2291.     var header = popup.ownerDocument.createElement("label");
  2292.     header.setAttribute("class", "menuHeader");
  2293.  
  2294.     var label = item.nol10n ? item.label : this.$STR(item.label);
  2295.  
  2296.     header.setAttribute("value", label);
  2297.  
  2298.     popup.appendChild(header);
  2299.     return header;
  2300. };
  2301.  
  2302. this.createMenuSeparator = function(popup, before)
  2303. {
  2304.     if (!popup.firstChild)
  2305.         return;
  2306.  
  2307.     var menuitem = popup.ownerDocument.createElement("menuseparator");
  2308.     if (before)
  2309.         popup.insertBefore(menuitem, before);
  2310.     else
  2311.         popup.appendChild(menuitem);
  2312.     return menuitem;
  2313. };
  2314.  
  2315. this.optionMenu = function(label, option)
  2316. {
  2317.     return {label: label, type: "checkbox", checked: Firebug[option], option: option,
  2318.         command: this.bindFixed(Firebug.setPref, Firebug, Firebug.prefDomain, option, !Firebug[option]) };
  2319. };
  2320.  
  2321. this.serviceOptionMenu = function(label, option)
  2322. {
  2323.     return {label: label, type: "checkbox", checked: Firebug[option], option: option,
  2324.         command: this.bindFixed(Firebug.setPref, Firebug, Firebug.servicePrefDomain, option, !Firebug[option]) };
  2325. };
  2326.  
  2327. // ************************************************************************************************
  2328. // Stack Traces
  2329.  
  2330. this.getCurrentStackTrace = function(context)
  2331. {
  2332.     var trace = null;
  2333.  
  2334.     Firebug.Debugger.halt(function(frame)
  2335.     {
  2336.         trace = FBL.getCorrectedStackTrace(frame, context);
  2337.     });
  2338.  
  2339.     return trace;
  2340. };
  2341.  
  2342. this.getCurrentJSDStackDump = function()
  2343. {
  2344.     var trace = null;
  2345.  
  2346.     Firebug.Debugger.halt(function(frame)
  2347.     {
  2348.         trace = FBL.getJSDStackDump(frame);
  2349.     });
  2350.  
  2351.     return trace;
  2352. };
  2353.  
  2354.  
  2355. this.getStackTrace = deprecated("name change for self-documentation", this.getCorrectedStackTrace);
  2356.  
  2357. this.getCorrectedStackTrace = function(frame, context)
  2358. {
  2359.     var trace = new this.StackTrace();
  2360.  
  2361.     for (; frame && frame.isValid; frame = frame.callingFrame)
  2362.     {
  2363.         if (!(Firebug.filterSystemURLs && this.isSystemURL(FBL.normalizeURL(frame.script.fileName))))
  2364.         {
  2365.             var stackFrame = this.getStackFrame(frame, context);
  2366.             if (stackFrame)
  2367.                 trace.frames.push(stackFrame);
  2368.         }
  2369.         else
  2370.         {
  2371.         }
  2372.     }
  2373.  
  2374.     if (trace.frames.length > 100)
  2375.     {
  2376.         var originalLength = trace.frames.length;
  2377.         trace.frames.splice(50, originalLength - 100);
  2378.         var excuse = "(eliding "+(originalLength - 100)+" frames)";
  2379.         trace.frames[50] = new this.StackFrame(context, excuse, null, excuse, 0, []);
  2380.     }
  2381.  
  2382.     return trace;
  2383. };
  2384.  
  2385. this.getStackFrame = function(frame, context)
  2386. {
  2387.     if (frame.isNative || frame.isDebugger)
  2388.     {
  2389.         var excuse = (frame.isNative) ?  "(native)" : "(debugger)";
  2390.         return new this.StackFrame(context, excuse, null, excuse, 0, []);
  2391.     }
  2392.     try
  2393.     {
  2394.         var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, frame.script);
  2395.         if (sourceFile)
  2396.         {
  2397.             var url = sourceFile.href;
  2398.             var analyzer = sourceFile.getScriptAnalyzer(frame.script);
  2399.  
  2400.             var lineNo = analyzer.getSourceLineFromFrame(context, frame);
  2401.             var fncSpec = analyzer.getFunctionDescription(frame.script, context, frame);
  2402.             if (!fncSpec.name)
  2403.                 fncSpec.name = frame.script.functionName;
  2404.  
  2405.             return new this.StackFrame(context, fncSpec.name, frame.script, url, lineNo, fncSpec.args, frame.pc);
  2406.         }
  2407.         else
  2408.         {
  2409.             var script = frame.script;
  2410.  
  2411.             return new this.StackFrame(context, script.functionName, frame.script, FBL.normalizeURL(script.fileName), frame.line, [], frame.pc);
  2412.         }
  2413.     }
  2414.     catch (exc)
  2415.     {
  2416.         return null;
  2417.     }
  2418. };
  2419.  
  2420. function getStackDump()
  2421. {
  2422.     var lines = [];
  2423.     for (var frame = Components.stack; frame; frame = frame.caller)
  2424.         lines.push(frame.filename + " (" + frame.lineNumber + ")");
  2425.  
  2426.     return lines.join("\n");
  2427. };
  2428. this.getStackDump = getStackDump;
  2429.  
  2430. this.getJSDStackDump = function(newestFrame)
  2431. {
  2432.     var lines = [];
  2433.     for (var frame = newestFrame; frame; frame = frame.callingFrame)
  2434.         lines.push(frame.script.fileName + " (" + frame.line + ")");
  2435.  
  2436.     return lines.join("\n");
  2437. };
  2438.  
  2439. this.getStackSourceLink = function()
  2440. {
  2441.     for (var frame = Components.stack; frame; frame = frame.caller)
  2442.     {
  2443.         if (frame.filename && frame.filename.indexOf("chrome://firebug/") == 0)
  2444.         {
  2445.             for (; frame; frame = frame.caller)
  2446.             {
  2447.                 var firebugComponent = "/components/firebug-";
  2448.                 if (frame.filename && frame.filename.indexOf("chrome://firebug/") != 0 &&
  2449.                     frame.filename.indexOf(firebugComponent) == -1)
  2450.                     break;
  2451.             }
  2452.             break;
  2453.         }
  2454.     }
  2455.     return this.getFrameSourceLink(frame);
  2456. }
  2457.  
  2458. this.getFrameSourceLink = function(frame)
  2459. {
  2460.     if (frame && frame.filename && frame.filename.indexOf("XPCSafeJSObjectWrapper") == -1)
  2461.         return new FBL.SourceLink(frame.filename, frame.lineNumber, "js");
  2462.     else
  2463.         return null;
  2464. };
  2465.  
  2466. this.getStackFrameId = function()
  2467. {
  2468.     for (var frame = Components.stack; frame; frame = frame.caller)
  2469.     {
  2470.         if (frame.languageName == "JavaScript"
  2471.             && !(frame.filename && frame.filename.indexOf("chrome://firebug/") == 0))
  2472.         {
  2473.             return frame.filename + "/" + frame.lineNumber;
  2474.         }
  2475.     }
  2476.     return null;
  2477. };
  2478.  
  2479. // ************************************************************************************************
  2480. // Event Monitoring
  2481.  
  2482. this.toggleMonitorEvents = function(object, type, state, context)
  2483. {
  2484.     if (state)
  2485.         this.unmonitorEvents(object, type, context);
  2486.     else
  2487.         this.monitorEvents(object, type, context);
  2488. };
  2489.  
  2490. this.monitorEvents = function(object, type, context)
  2491. {
  2492.     if (!this.areEventsMonitored(object, type, context) && object && object.addEventListener)
  2493.     {
  2494.         if (!context.onMonitorEvent)
  2495.             context.onMonitorEvent = function(event) { Firebug.Console.log(event, context); };
  2496.  
  2497.         if (!context.eventsMonitored)
  2498.             context.eventsMonitored = [];
  2499.  
  2500.         context.eventsMonitored.push({object: object, type: type});
  2501.  
  2502.         if (!type)
  2503.             this.attachAllListeners(object, context.onMonitorEvent, context);
  2504.         else
  2505.             object.addEventListener(type, context.onMonitorEvent, false);
  2506.     }
  2507. };
  2508.  
  2509. this.unmonitorEvents = function(object, type, context)
  2510. {
  2511.     var eventsMonitored = context.eventsMonitored;
  2512.  
  2513.     for (var i = 0; i < eventsMonitored.length; ++i)
  2514.     {
  2515.         if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
  2516.         {
  2517.             eventsMonitored.splice(i, 1);
  2518.  
  2519.             if (!type)
  2520.                 this.detachAllListeners(object, context.onMonitorEvent, context);
  2521.             else
  2522.                 object.removeEventListener(type, context.onMonitorEvent, false);
  2523.             break;
  2524.         }
  2525.     }
  2526. };
  2527.  
  2528. this.areEventsMonitored = function(object, type, context)
  2529. {
  2530.     var eventsMonitored = context.eventsMonitored;
  2531.     if (eventsMonitored)
  2532.     {
  2533.         for (var i = 0; i < eventsMonitored.length; ++i)
  2534.         {
  2535.             if (eventsMonitored[i].object == object && eventsMonitored[i].type == type)
  2536.                 return true;
  2537.         }
  2538.     }
  2539.  
  2540.     return false;
  2541. };
  2542.  
  2543. // ************************************************************************************************
  2544. // Functions
  2545.  
  2546. this.findScripts = function(context, url, line)
  2547. {
  2548.     var sourceFile = context.sourceFileMap[url];
  2549.     if (sourceFile)
  2550.         var scripts = sourceFile.scriptsIfLineCouldBeExecutable(line);
  2551.     else
  2552.     {
  2553.     }
  2554.     return scripts;
  2555. };
  2556.  
  2557. this.findScriptForFunctionInContext = function(context, fn)
  2558. {
  2559.     var found = null;
  2560.  
  2561.     if (!fn || !fn.toString)
  2562.         return found;
  2563.  
  2564.     var fns = fn.toSource();
  2565.     var found = this.forEachFunction(context, function findMatchingScript(script, aFunction)
  2566.     {
  2567.         if (!aFunction['toSource'] || typeof(aFunction['toSource']) != "function")
  2568.             return;
  2569.         try {
  2570.             var tfs = aFunction.toSource();
  2571.         } catch (etfs) {
  2572.             FBTrace.sysout("unwrapped.toSource fails for unwrapped: "+etfs, aFunction);
  2573.         }
  2574.  
  2575.         if (tfs == fns)
  2576.             return script;
  2577.     });
  2578.  
  2579.     return found;
  2580. }
  2581.  
  2582. this.forEachFunction = function(context, cb)
  2583. {
  2584.     for (var url in context.sourceFileMap)
  2585.     {
  2586.         var sourceFile = context.sourceFileMap[url];
  2587.         var rc = sourceFile.forEachScript(function seekFn(script, sourceFile)
  2588.         {
  2589.             if (!script.isValid)
  2590.                 return;
  2591.             try
  2592.             {
  2593.                 var testFunctionObject = script.functionObject;  // Boris says this object is bogus.
  2594.                 if (!testFunctionObject.isValid)
  2595.                     return false;
  2596.                 var theFunction = FBL.unwrapIValue(testFunctionObject);
  2597.                 if (theFunction)
  2598.                 {
  2599.                     var rc = cb(script, theFunction, sourceFile);
  2600.                     if (rc)
  2601.                         return rc;
  2602.                 }
  2603.             }
  2604.             catch(exc)
  2605.             {
  2606.             }
  2607.         });
  2608.         if (rc)
  2609.             return rc;
  2610.     }
  2611.     return false;
  2612. }
  2613.  
  2614. this.findScriptForFunction = function(fn)
  2615. {
  2616.     var found = {tag: "not set"};
  2617.  
  2618.     this.jsd.enumerateScripts({enumerateScript: function findScriptMatchingFn(script)
  2619.     {
  2620.         try {
  2621.             if (script.isValid)
  2622.             {
  2623.  
  2624.                 var iValueFunctionObject = script.functionObject;
  2625.                 //FBTrace.dumpIValue("lib.findScriptForFunction iValueFunctionObject", iValueFunctionObject);
  2626.                 var testFunctionObject = FBL.unwrapIValue(script.functionObject);
  2627.                 if (testFunctionObject instanceof Function)
  2628.                     FBTrace.sysout("lib.findScriptForFunction testFunctionObject "+testFunctionObject+" vs "+fn+"\n");
  2629.                 if (testFunctionObject == fn)
  2630.                 {
  2631.                     found = script;
  2632.                     return;
  2633.                 }
  2634.             }
  2635.         } catch (exc) {
  2636.         }
  2637.     }});
  2638.  
  2639.     FBTrace.sysout("findScriptForFunction found ", found.tag);
  2640.     return found;
  2641. };
  2642.  
  2643. this.findSourceForFunction = function(fn, context)
  2644. {
  2645.     var script = this.findScriptForFunctionInContext(context, fn);
  2646.     return (script)? this.getSourceLinkForScript(script, context) : null;
  2647. };
  2648.  
  2649. this.getSourceLinkForScript = function(script, context)
  2650. {
  2651.     var sourceFile = Firebug.SourceFile.getSourceFileByScript(context, script);
  2652.     if (sourceFile)
  2653.     {
  2654.         var scriptAnalyzer = sourceFile.getScriptAnalyzer(script);
  2655.         if (scriptAnalyzer)
  2656.             return scriptAnalyzer.getSourceLinkForScript(script);
  2657.         else
  2658.         {
  2659.             // no-op for detrace
  2660.         }
  2661.     }
  2662. };
  2663.  
  2664. this.getFunctionName = function(script, context, frame, noArgs)
  2665. {
  2666.     if (!script)
  2667.     {
  2668.         return "(no script)";
  2669.     }
  2670.     var name = script.functionName;
  2671.  
  2672.     if (!name || (name == "anonymous"))
  2673.     {
  2674.         var analyzer = Firebug.SourceFile.getScriptAnalyzer(context, script);
  2675.         if (analyzer)
  2676.         {
  2677.             var functionSpec = analyzer.getFunctionDescription(script, context, frame);
  2678.             name = functionSpec.name + (noArgs ? "" : "("+functionSpec.args.join(',')+")");
  2679.         }
  2680.         else
  2681.         {
  2682.             name =  this.guessFunctionName(FBL.normalizeURL(script.fileName), script.baseLineNumber, context);
  2683.         }
  2684.     }
  2685.     return name;
  2686. };
  2687.  
  2688. this.guessFunctionName = function(url, lineNo, context)
  2689. {
  2690.     if (context)
  2691.     {
  2692.         if (context.sourceCache)
  2693.             return this.guessFunctionNameFromLines(url, lineNo, context.sourceCache);
  2694.     }
  2695.     return "? in "+this.getFileName(url)+"@"+lineNo;
  2696. };
  2697.  
  2698. this.guessFunctionNameFromLines = function(url, lineNo, sourceCache)
  2699. {
  2700.     // Walk backwards from the first line in the function until we find the line which
  2701.     // matches the pattern above, which is the function definition
  2702.     var line = "";
  2703.     for (var i = 0; i < 4; ++i)
  2704.     {
  2705.         line = sourceCache.getLine(url, lineNo-i) + line;
  2706.         if (line != undefined)
  2707.         {
  2708.             var m = reGuessFunction.exec(line);
  2709.             if (m)
  2710.                 return m[1];
  2711.             else
  2712.             {
  2713.             }
  2714.             m = reFunctionArgNames.exec(line);
  2715.             if (m && m[1])
  2716.                 return m[1];
  2717.         }
  2718.     }
  2719.     return "(?)";
  2720. };
  2721.  
  2722. this.getFunctionArgNames = function(frame)
  2723. {
  2724.     var script = frame.script;
  2725.     if (script.getParameterNames)  // FF 3.6 or later
  2726.     {
  2727.         return script.getParameterNames();
  2728.     }
  2729.     else // FF 3.5 or eariler
  2730.     {
  2731.         return []; // TODO
  2732.         var m = reFunctionArgNames.exec(this.safeToString(fn));
  2733.         if (m)
  2734.         {
  2735.             var argNames = m[2].split(", ");
  2736.             if (argNames.length && argNames[0])
  2737.                 return argNames;
  2738.         }
  2739.         return [];
  2740.     }
  2741. };
  2742.  
  2743. this.getFunctionArgValues = function(frame)
  2744. {
  2745.     var values = [];
  2746.  
  2747.     var argNames = this.getFunctionArgNames(frame);
  2748.     var scope = FBL.unwrapIValue(frame.scope);
  2749.  
  2750.     for (var i = 0; i < argNames.length; ++i)
  2751.     {
  2752.         var argName = argNames[i];
  2753.         if (scope)
  2754.         {
  2755.             var pvalue = scope.argName;
  2756.             var value = pvalue ? FBL.unwrapIValue(pvalue.value) : undefined;
  2757.             values.push({name: argName, value: value});
  2758.         }
  2759.         else
  2760.         {
  2761.             values.push({name: argName});
  2762.         }
  2763.     }
  2764.  
  2765.     return values;
  2766. };
  2767.  
  2768. // ************************************************************************************************
  2769. // Source Files
  2770.  
  2771. this.getSourceFileByHref = function(url, context)
  2772. {
  2773.     return context.sourceFileMap[url];
  2774. };
  2775.  
  2776. this.getAllStyleSheets = function(context)
  2777. {
  2778.     var styleSheets = [];
  2779.  
  2780.     function addSheet(sheet)
  2781.     {
  2782.         var sheetLocation =  FBL.getURLForStyleSheet(sheet);
  2783.  
  2784.         if (!Firebug.showUserAgentCSS && FBL.isSystemURL(sheetLocation))
  2785.             return;
  2786.  
  2787.         styleSheets.push(sheet);
  2788.         try
  2789.         {
  2790.             for (var i = 0; i < sheet.cssRules.length; ++i)
  2791.             {
  2792.                 var rule = sheet.cssRules[i];
  2793.                 if (rule instanceof CSSImportRule)
  2794.                     addSheet(rule.styleSheet);
  2795.             }
  2796.         }
  2797.         catch(e)
  2798.         {
  2799.         }
  2800.     }
  2801.  
  2802.     this.iterateWindows(context.window, function(subwin)
  2803.     {
  2804.         var rootSheets = subwin.document.styleSheets;
  2805.         for (var i = 0; i < rootSheets.length; ++i)
  2806.             addSheet(rootSheets[i]);
  2807.     });
  2808.  
  2809.     return styleSheets;
  2810. };
  2811.  
  2812. this.getStyleSheetByHref = function(url, context)
  2813. {
  2814.     if (!context.styleSheetMap)
  2815.         FBL.createStyleSheetMap(context);  // fill cache
  2816.  
  2817.     return context.styleSheetMap.hasOwnProperty(url) ? context.styleSheetMap[url] : undefined;
  2818. };
  2819.  
  2820. this.createStyleSheetMap = function(context)
  2821. {
  2822.     context.styleSheetMap = {};
  2823.  
  2824.     function addSheet(sheet)
  2825.     {
  2826.         var sheetURL = FBL.getURLForStyleSheet(sheet);
  2827.         context.styleSheetMap[sheetURL] = sheet;
  2828.  
  2829.         for (var i = 0; i < sheet.cssRules.length; ++i)
  2830.         {
  2831.             var rule = sheet.cssRules[i];
  2832.             if (rule instanceof CSSStyleRule)
  2833.             {
  2834.                 if (rule.type == CSSRule.STYLE_RULE)  // once we get here no more imports
  2835.                     return;
  2836.             }
  2837.             else if (rule instanceof CSSImportRule)
  2838.             {
  2839.                 addSheet(rule.styleSheet);
  2840.             }
  2841.         }
  2842.     }
  2843.  
  2844.     this.iterateWindows(context.window, function(subwin)
  2845.     {
  2846.         var rootSheets = subwin.document.styleSheets;
  2847.         for (var i = 0; i < rootSheets.length; ++i)
  2848.         {
  2849.             addSheet(rootSheets[i]);
  2850.         }
  2851.     });
  2852.  
  2853.     return context.styleSheetMap;
  2854. };
  2855.  
  2856. this.sourceURLsAsArray = function(context)
  2857. {
  2858.     var urls = [];
  2859.     var sourceFileMap = context.sourceFileMap;
  2860.     for (var url in sourceFileMap)
  2861.         urls.push(url);
  2862.  
  2863.     return urls;
  2864. };
  2865.  
  2866. this.sourceFilesAsArray = function(sourceFileMap)
  2867. {
  2868.     var sourceFiles = [];
  2869.     for (var url in sourceFileMap)
  2870.         sourceFiles.push(sourceFileMap[url]);
  2871.     return sourceFiles;
  2872. };
  2873.  
  2874.  
  2875. // ************************************************************************************************
  2876. // Firefox browsing
  2877.  
  2878. this.openNewTab = function(url, postText)
  2879. {
  2880.     if (!url)
  2881.         return;
  2882.  
  2883.     var postData = null;
  2884.     if (postText)
  2885.     {
  2886.         var stringStream = this.getInputStreamFromString(postText);
  2887.         postData = this.CCIN("@mozilla.org/network/mime-input-stream;1", "nsIMIMEInputStream");
  2888.         postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
  2889.         postData.addContentLength = true;
  2890.         postData.setData(stringStream);
  2891.     }
  2892.  
  2893.     gBrowser.selectedTab = gBrowser.addTab(url, null, null, postData);
  2894. };
  2895.  
  2896. this.openWindow = function(windowType, url, features, params)
  2897. {
  2898.     var win = windowType ? wm.getMostRecentWindow(windowType) : null;
  2899.     if (win) {
  2900.       if ("initWithParams" in win)
  2901.         win.initWithParams(params);
  2902.       win.focus();
  2903.     }
  2904.     else {
  2905.       var winFeatures = "resizable,dialog=no,centerscreen" + (features != "" ? ("," + features) : "");
  2906.       var parentWindow = (this.instantApply || !window.opener || window.opener.closed) ? window : window.opener;
  2907.       win = parentWindow.openDialog(url, "_blank", winFeatures, params);
  2908.     }
  2909.     return win;
  2910. };
  2911.  
  2912. this.viewSource = function(url, lineNo)
  2913. {
  2914.     window.openDialog("chrome://global/content/viewSource.xul", "_blank",
  2915.         "all,dialog=no", url, null, null, lineNo);
  2916. };
  2917.  
  2918. // Iterate over all opened firefox windows of the given type. If the callback returns true
  2919. // the iteration is stopped.
  2920. this.iterateBrowserWindows = function(windowType, callback)
  2921. {
  2922.     var windowList = wm.getZOrderDOMWindowEnumerator(windowType, true);
  2923.     if (!windowList.hasMoreElements())
  2924.         windowList = wm.getEnumerator(windowType);
  2925.  
  2926.     while (windowList.hasMoreElements()) {
  2927.         if (callback(windowList.getNext()))
  2928.             return true;
  2929.     }
  2930.  
  2931.     return false;
  2932. };
  2933.  
  2934. this.iterateBrowserTabs = function(browserWindow, callback)
  2935. {
  2936.     var tabBrowser = browserWindow.getBrowser();
  2937.     var numTabs = tabBrowser.browsers.length;
  2938.     for(var index=0; index<numTabs; index++)
  2939.     {
  2940.         var currentBrowser = tabBrowser.getBrowserAtIndex(index);
  2941.         if (callback(tabBrowser.mTabs[index], currentBrowser))
  2942.             return true;
  2943.     }
  2944.  
  2945.     return false;
  2946. }
  2947.  
  2948. this.safeGetWindowLocation = function(window)
  2949. {
  2950.     try
  2951.     {
  2952.         if (window)
  2953.         {
  2954.             if (window.closed)
  2955.                 return "about:closed";
  2956.             if ("location" in window)
  2957.             {
  2958.                 if ("toString" in window.location)
  2959.                     return window.location.toString();
  2960.                 else
  2961.                     return "(window.location has no toString)";
  2962.             }
  2963.             else
  2964.                 return "(no window.location)";
  2965.         }
  2966.         else
  2967.             return "(no context.window)";
  2968.     }
  2969.     catch(exc)
  2970.     {
  2971.             FBTrace.sysout("TabContext.getWindowLocation failed window:", window);
  2972.         return "(getWindowLocation: "+exc+")";
  2973.     }
  2974. };
  2975.  
  2976. this.safeGetRequestName = function(request)
  2977. {
  2978.     try
  2979.     {
  2980.         return request.name;
  2981.     }
  2982.     catch (exc)
  2983.     {
  2984.     }
  2985.  
  2986.     return null;
  2987. }
  2988.  
  2989. this.safeGetContentType = function(request)
  2990. {
  2991.     try
  2992.     {
  2993.         return new String(request.contentType).toLowerCase();
  2994.     }
  2995.     catch (err)
  2996.     {
  2997.     }
  2998.  
  2999.     return null;
  3000. }
  3001.  
  3002. // ************************************************************************************************
  3003. // JavaScript Parsing
  3004.  
  3005. this.getExpressionAt = function(text, charOffset)
  3006. {
  3007.     var offset = 0;
  3008.     for (var m = reWord.exec(text); m; m = reWord.exec(text.substr(offset)))
  3009.     {
  3010.         var word = m[0];
  3011.         var wordOffset = offset+m.index;
  3012.         if (charOffset >= wordOffset && charOffset <= wordOffset+word.length)
  3013.         {
  3014.             var innerOffset = charOffset-wordOffset;
  3015.             var dots = word.substr(0, innerOffset).split(".").length;
  3016.             var subExpr = word.split(".").slice(0, dots).join(".");
  3017.             return {expr: subExpr, offset: wordOffset};
  3018.         }
  3019.  
  3020.         offset = wordOffset+word.length;
  3021.     }
  3022.  
  3023.     return {expr: null, offset: -1};
  3024. };
  3025.  
  3026. var jsKeywords =
  3027. {
  3028.     "var": 1,
  3029.     "const": 1,
  3030.     "class": 1,
  3031.     "extends": 1,
  3032.     "import": 1,
  3033.     "namespace": 1,
  3034.     "function": 1,
  3035.     "debugger": 1,
  3036.     "new": 1,
  3037.     "delete": 1,
  3038.     "null": 1,
  3039.     "undefined": 1,
  3040.     "true": 1,
  3041.     "false": 1,
  3042.     "void": 1,
  3043.     "typeof": 1,
  3044.     "instanceof": 1,
  3045.     "break": 1,
  3046.     "continue": 1,
  3047.     "return": 1,
  3048.     "throw": 1,
  3049.     "try": 1,
  3050.     "catch": 1,
  3051.     "finally": 1,
  3052.     "if": 1,
  3053.     "else": 1,
  3054.     "for": 1,
  3055.     "while": 1,
  3056.     "do": 1,
  3057.     "with": 1,
  3058.     "switch": 1,
  3059.     "case": 1,
  3060.     "default": 1
  3061. };
  3062.  
  3063. this.isJavaScriptKeyword = function(name)
  3064. {
  3065.     return name in jsKeywords;
  3066. };
  3067.  
  3068. // ************************************************************************************************
  3069. // Events
  3070.  
  3071. this.cancelEvent = function(event)
  3072. {
  3073.     event.stopPropagation();
  3074.     event.preventDefault();
  3075. };
  3076.  
  3077. this.isLeftClick = function(event)
  3078. {
  3079.     return event.button == 0 && this.noKeyModifiers(event);
  3080. };
  3081.  
  3082. this.isMiddleClick = function(event)
  3083. {
  3084.     return event.button == 1 && this.noKeyModifiers(event);
  3085. };
  3086.  
  3087. this.isRightClick = function(event)
  3088. {
  3089.     return event.button == 2 && this.noKeyModifiers(event);
  3090. };
  3091.  
  3092. this.noKeyModifiers = function(event)
  3093. {
  3094.     return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
  3095. };
  3096.  
  3097. this.isControlClick = function(event)
  3098. {
  3099.     return event.button == 0 && this.isControl(event);
  3100. };
  3101.  
  3102. this.isShiftClick = function(event)
  3103. {
  3104.     return event.button == 0 && this.isShift(event);
  3105. };
  3106.  
  3107. this.isControl = function(event)
  3108. {
  3109.     return (event.metaKey || event.ctrlKey) && !event.shiftKey && !event.altKey;
  3110. };
  3111.  
  3112. this.isAlt = function(event)
  3113. {
  3114.     return event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey;
  3115. };
  3116.  
  3117. this.isAltClick = function(event)
  3118. {
  3119.     return event.button == 0 && this.isAlt(event);
  3120. };
  3121.  
  3122. this.isControlShift = function(event)
  3123. {
  3124.     return (event.metaKey || event.ctrlKey) && event.shiftKey && !event.altKey;
  3125. };
  3126.  
  3127. this.isShift = function(event)
  3128. {
  3129.     return event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey;
  3130. };
  3131.  
  3132. this.dispatch = function(listeners, name, args)
  3133. {
  3134.     if (!listeners)
  3135.         return;
  3136.  
  3137.     try {
  3138.         for (var i = 0; i < listeners.length; ++i)
  3139.         {
  3140.             var listener = listeners[i];
  3141.             if ( listener[name] )
  3142.             {
  3143.                 //FBTrace.sysout("FBL.dispatch "+i+") "+name+" to "+listener.dispatchName);
  3144.                 try
  3145.                 {
  3146.                     listener[name].apply(listener, args);
  3147.                 }
  3148.                 catch(exc)
  3149.                 {
  3150.                 }
  3151.             }
  3152.             else
  3153.             {
  3154.             }
  3155.         }
  3156.     }
  3157.     catch (exc)
  3158.     {
  3159.     }
  3160. };
  3161.  
  3162. this.dispatch2 = function(listeners, name, args)
  3163. {
  3164.     try
  3165.     {
  3166.         for (var i = 0; i < listeners.length; ++i)
  3167.         {
  3168.             var listener = listeners[i];
  3169.             if ( listener.hasOwnProperty(name) )
  3170.             {
  3171.                 var result = listener[name].apply(listener, args);
  3172.                 if ( result )
  3173.                 {
  3174.                     return result;
  3175.                 }
  3176.             }
  3177.             else
  3178.             {
  3179.             }
  3180.         }
  3181.     }
  3182.     catch (exc)
  3183.     {
  3184.     }
  3185. };
  3186.  
  3187. // ************************************************************************************************
  3188. // DOM Events
  3189.  
  3190. const eventTypes =
  3191. {
  3192.     composition: [
  3193.         "composition",
  3194.         "compositionstart",
  3195.         "compositionend" ],
  3196.     contextmenu: [
  3197.         "contextmenu" ],
  3198.     drag: [
  3199.         "dragenter",
  3200.         "dragover",
  3201.         "dragexit",
  3202.         "dragdrop",
  3203.         "draggesture" ],
  3204.     focus: [
  3205.         "focus",
  3206.         "blur" ],
  3207.     form: [
  3208.         "submit",
  3209.         "reset",
  3210.         "change",
  3211.         "select",
  3212.         "input" ],
  3213.     key: [
  3214.         "keydown",
  3215.         "keyup",
  3216.         "keypress" ],
  3217.     load: [
  3218.         "load",
  3219.         "beforeunload",
  3220.         "unload",
  3221.         "abort",
  3222.         "error" ],
  3223.     mouse: [
  3224.         "mousedown",
  3225.         "mouseup",
  3226.         "click",
  3227.         "dblclick",
  3228.         "mouseover",
  3229.         "mouseout",
  3230.         "mousemove" ],
  3231.     mutation: [
  3232.         "DOMSubtreeModified",
  3233.         "DOMNodeInserted",
  3234.         "DOMNodeRemoved",
  3235.         "DOMNodeRemovedFromDocument",
  3236.         "DOMNodeInsertedIntoDocument",
  3237.         "DOMAttrModified",
  3238.         "DOMCharacterDataModified" ],
  3239.     paint: [
  3240.         "paint",
  3241.         "resize",
  3242.         "scroll" ],
  3243.     scroll: [
  3244.         "overflow",
  3245.         "underflow",
  3246.         "overflowchanged" ],
  3247.     text: [
  3248.         "text" ],
  3249.     ui: [
  3250.         "DOMActivate",
  3251.         "DOMFocusIn",
  3252.         "DOMFocusOut" ],
  3253.     xul: [
  3254.         "popupshowing",
  3255.         "popupshown",
  3256.         "popuphiding",
  3257.         "popuphidden",
  3258.         "close",
  3259.         "command",
  3260.         "broadcast",
  3261.         "commandupdate" ]
  3262. };
  3263.  
  3264. this.getEventFamily = function(eventType)
  3265. {
  3266.     if (!this.families)
  3267.     {
  3268.         this.families = {};
  3269.  
  3270.         for (var family in eventTypes)
  3271.         {
  3272.             var types = eventTypes[family];
  3273.             for (var i = 0; i < types.length; ++i)
  3274.                 this.families[types[i]] = family;
  3275.         }
  3276.     }
  3277.  
  3278.     return this.families[eventType];
  3279. };
  3280.  
  3281. this.attachAllListeners = function(object, listener)
  3282. {
  3283.     for (var family in eventTypes)
  3284.     {
  3285.         if (family != "mutation" || Firebug.attachMutationEvents)
  3286.             this.attachFamilyListeners(family, object, listener);
  3287.     }
  3288. };
  3289.  
  3290. this.detachAllListeners = function(object, listener)
  3291. {
  3292.     for (var family in eventTypes)
  3293.     {
  3294.         if (family != "mutation" || Firebug.attachMutationEvents)
  3295.             this.detachFamilyListeners(family, object, listener);
  3296.     }
  3297. };
  3298.  
  3299. this.attachFamilyListeners = function(family, object, listener)
  3300. {
  3301.     var types = eventTypes[family];
  3302.     for (var i = 0; i < types.length; ++i)
  3303.         object.addEventListener(types[i], listener, false);
  3304. };
  3305.  
  3306. this.detachFamilyListeners = function(family, object, listener)
  3307. {
  3308.     var types = eventTypes[family];
  3309.     for (var i = 0; i < types.length; ++i)
  3310.         object.removeEventListener(types[i], listener, false);
  3311. };
  3312.  
  3313. // ************************************************************************************************
  3314. // URLs
  3315.  
  3316. this.getFileName = function(url)
  3317. {
  3318.     var split = this.splitURLBase(url);
  3319.     return split.name;
  3320. };
  3321.  
  3322. this.splitURLBase = function(url)
  3323. {
  3324.     if (this.isDataURL(url))
  3325.         return this.splitDataURL(url);
  3326.     return this.splitURLTrue(url);
  3327. };
  3328.  
  3329. this.splitDataURL = function(url)
  3330. {
  3331.     var mark = url.indexOf('data:');
  3332.     if (mark != 0)
  3333.         return false; //  the first 5 chars must be 'data:'
  3334.  
  3335.     var point = url.indexOf(',', 5);
  3336.     if (point < 5)
  3337.         return false; // syntax error
  3338.  
  3339.     var props = { encodedContent: url.substr(point+1) };
  3340.  
  3341.     var metadataBuffer = url.substring(5, point);
  3342.     var metadata = metadataBuffer.split(';');
  3343.     for (var i = 0; i < metadata.length; i++)
  3344.     {
  3345.         var nv = metadata[i].split('=');
  3346.         if (nv.length == 2)
  3347.             props[nv[0]] = nv[1];
  3348.     }
  3349.  
  3350.     // Additional Firebug-specific properties
  3351.     if (props.hasOwnProperty('fileName'))
  3352.     {
  3353.          var caller_URL = decodeURIComponent(props['fileName']);
  3354.          var caller_split = this.splitURLTrue(caller_URL);
  3355.  
  3356.          props['fileName'] = caller_URL;
  3357.  
  3358.         if (props.hasOwnProperty('baseLineNumber'))  // this means it's probably an eval()
  3359.         {
  3360.             props['path'] = caller_split.path;
  3361.             props['line'] = props['baseLineNumber'];
  3362.             var hint = decodeURIComponent(props['encodedContent']).substr(0,200).replace(/\s*$/, "");
  3363.             props['name'] =  'eval->'+hint;
  3364.         }
  3365.         else
  3366.         {
  3367.             props['name'] = caller_split.name;
  3368.             props['path'] = caller_split.path;
  3369.         }
  3370.     }
  3371.     else
  3372.     {
  3373.         if (!props.hasOwnProperty('path'))
  3374.             props['path'] = "data:";
  3375.         if (!props.hasOwnProperty('name'))
  3376.             props['name'] =  decodeURIComponent(props['encodedContent']).substr(0,200).replace(/\s*$/, "");
  3377.     }
  3378.  
  3379.     return props;
  3380. };
  3381.  
  3382. this.splitURLTrue = function(url)
  3383. {
  3384.     var m = reSplitFile.exec(url);
  3385.     if (!m)
  3386.         return {name: url, path: url};
  3387.     else if (!m[2])
  3388.         return {path: m[1], name: m[1]};
  3389.     else
  3390.         return {path: m[1], name: m[2]+m[3]};
  3391. };
  3392.  
  3393. this.getFileExtension = function(url)
  3394. {
  3395.     if (!url)
  3396.         return null;
  3397.  
  3398.     // Remove query string from the URL if any.
  3399.     var queryString = url.indexOf("?");
  3400.     if (queryString != -1)
  3401.         url = url.substr(0, queryString);
  3402.  
  3403.     // Now get the file extension.
  3404.     var lastDot = url.lastIndexOf(".");
  3405.     return url.substr(lastDot+1);
  3406. };
  3407.  
  3408. this.isSystemURL = function(url)
  3409. {
  3410.     if (!url) return true;
  3411.     if (url.length == 0) return true;
  3412.     if (url[0] == 'h') return false;
  3413.     if (url.substr(0, 9) == "resource:")
  3414.         return true;
  3415.     else if (url.substr(0, 16) == "chrome://firebug")
  3416.         return true;
  3417.     else if (url  == "XPCSafeJSObjectWrapper.cpp")
  3418.         return true;
  3419.     else if (url.substr(0, 6) == "about:")
  3420.         return true;
  3421.     else if (url.indexOf("firebug-service.js") != -1)
  3422.         return true;
  3423.     else
  3424.         return false;
  3425. };
  3426.  
  3427. this.isSystemPage = function(win)
  3428. {
  3429.     try
  3430.     {
  3431.         var doc = win.document;
  3432.         if (!doc)
  3433.             return false;
  3434.  
  3435.         // Detect pages for pretty printed XML
  3436.         if ((doc.styleSheets.length && doc.styleSheets[0].href
  3437.                 == "chrome://global/content/xml/XMLPrettyPrint.css")
  3438.             || (doc.styleSheets.length > 1 && doc.styleSheets[1].href
  3439.                 == "chrome://browser/skin/feeds/subscribe.css"))
  3440.             return true;
  3441.  
  3442.         return FBL.isSystemURL(win.location.href);
  3443.     }
  3444.     catch (exc)
  3445.     {
  3446.         // Sometimes documents just aren't ready to be manipulated here, but don't let that
  3447.         // gum up the works
  3448.         ERROR("tabWatcher.isSystemPage document not ready:"+ exc);
  3449.         return false;
  3450.     }
  3451. }
  3452.  
  3453. this.isSystemStyleSheet = function(sheet)
  3454. {
  3455.     var href = sheet && sheet.href;
  3456.     return href && FBL.isSystemURL(href);
  3457. };
  3458.  
  3459. this.getURIHost = function(uri)
  3460. {
  3461.     try
  3462.     {
  3463.         if (uri)
  3464.             return uri.host;
  3465.         else
  3466.             return "";
  3467.     }
  3468.     catch (exc)
  3469.     {
  3470.         return "";
  3471.     }
  3472. }
  3473.  
  3474. this.isLocalURL = function(url)
  3475. {
  3476.     if (url.substr(0, 5) == "file:")
  3477.         return true;
  3478.     else if (url.substr(0, 8) == "wyciwyg:")
  3479.         return true;
  3480.     else
  3481.         return false;
  3482. };
  3483.  
  3484. this.isDataURL = function(url)
  3485. {
  3486.     return (url && url.substr(0,5) == "data:");
  3487. };
  3488.  
  3489. this.getLocalPath = function(url)
  3490. {
  3491.     if (this.isLocalURL(url))
  3492.     {
  3493.         var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  3494.         var file = fileHandler.getFileFromURLSpec(url);
  3495.         return file.path;
  3496.     }
  3497. };
  3498.  
  3499. this.getURLFromLocalFile = function(file)
  3500. {
  3501.     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  3502.     var URL = fileHandler.getURLSpecFromFile(file);
  3503.     return URL;
  3504. };
  3505.  
  3506. this.getDataURLForContent = function(content, url)
  3507. {
  3508.     // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
  3509.     var uri = "data:text/html;";
  3510.     uri += "fileName="+encodeURIComponent(url)+ ","
  3511.     uri += encodeURIComponent(content);
  3512.     return uri;
  3513. },
  3514.  
  3515. this.getDomain = function(url)
  3516. {
  3517.     var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url);
  3518.     return m ? m[1] : "";
  3519. };
  3520.  
  3521. this.getURLPath = function(url)
  3522. {
  3523.     var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url);
  3524.     return m ? m[1] : "";
  3525. };
  3526.  
  3527. this.getPrettyDomain = function(url)
  3528. {
  3529.     var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url);
  3530.     return m ? m[2] : "";
  3531. };
  3532.  
  3533. this.absoluteURL = function(url, baseURL)
  3534. {
  3535.     return this.absoluteURLWithDots(url, baseURL).replace("/./", "/", "g");
  3536. };
  3537.  
  3538. this.absoluteURLWithDots = function(url, baseURL)
  3539. {
  3540.     if (url[0] == "?")
  3541.         return baseURL + url;
  3542.  
  3543.     var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/;
  3544.     var m = reURL.exec(url);
  3545.     if (m)
  3546.         return url;
  3547.  
  3548.     var m = reURL.exec(baseURL);
  3549.     if (!m)
  3550.         return "";
  3551.  
  3552.     var head = m[1];
  3553.     var tail = m[3];
  3554.     if (url.substr(0, 2) == "//")
  3555.         return m[2] + url;
  3556.     else if (url[0] == "/")
  3557.     {
  3558.         return head + url;
  3559.     }
  3560.     else if (tail[tail.length-1] == "/")
  3561.         return baseURL + url;
  3562.     else
  3563.     {
  3564.         var parts = tail.split("/");
  3565.         return head + parts.slice(0, parts.length-1).join("/") + "/" + url;
  3566.     }
  3567. }
  3568.  
  3569. this.normalizeURL = function(url)  // this gets called a lot, any performance improvement welcome
  3570. {
  3571.     if (!url)
  3572.         return "";
  3573.     // Replace one or more characters that are not forward-slash followed by /.., by space.
  3574.     if (url.length < 255) // guard against monsters.
  3575.     {
  3576.         // Replace one or more characters that are not forward-slash followed by /.., by space.
  3577.         url = url.replace(/[^/]+\/\.\.\//, "", "g");
  3578.         // Issue 1496, avoid #
  3579.         url = url.replace(/#.*/,"");
  3580.         // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they
  3581.         // don't match up with the URLs we get back from the DOM
  3582.         url = url.replace(/file:\/([^/])/g, "file:///$1");
  3583.         if (url.indexOf('chrome:')==0)
  3584.         {
  3585.             var m = reChromeCase.exec(url);  // 1 is package name, 2 is path
  3586.             if (m)
  3587.             {
  3588.                 url = "chrome://"+m[1].toLowerCase()+"/"+m[2];
  3589.             }
  3590.         }
  3591.     }
  3592.     return url;
  3593. };
  3594.  
  3595. this.denormalizeURL = function(url)
  3596. {
  3597.     return url.replace(/file:\/\/\//g, "file:/");
  3598. };
  3599.  
  3600. this.parseURLParams = function(url)
  3601. {
  3602.     var q = url ? url.indexOf("?") : -1;
  3603.     if (q == -1)
  3604.         return [];
  3605.  
  3606.     var search = url.substr(q+1);
  3607.     var h = search.lastIndexOf("#");
  3608.     if (h != -1)
  3609.         search = search.substr(0, h);
  3610.  
  3611.     if (!search)
  3612.         return [];
  3613.  
  3614.     return this.parseURLEncodedText(search);
  3615. };
  3616.  
  3617. this.parseURLEncodedText = function(text, noLimit)
  3618. {
  3619.     const maxValueLength = 25000;
  3620.  
  3621.     var params = [];
  3622.  
  3623.     // Unescape '+' characters that are used to encode a space.
  3624.     // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt
  3625.     text = text.replace(/\+/g, " ");
  3626.  
  3627.     function decodeText(text)
  3628.     {
  3629.         try
  3630.         {
  3631.             return decodeURIComponent(text);
  3632.         }
  3633.         catch (e)
  3634.         {
  3635.             return decodeURIComponent(unescape(text));
  3636.         }
  3637.     }
  3638.  
  3639.     var args = text.split("&");
  3640.     for (var i = 0; i < args.length; ++i)
  3641.     {
  3642.         try
  3643.         {
  3644.             var index = args[i].indexOf("=");
  3645.             if (index != -1)
  3646.             {
  3647.                 var paramName = args[i].substring(0, index);
  3648.                 var paramValue = args[i].substring(index + 1);
  3649.  
  3650.                 if (paramValue.length > maxValueLength && !noLimit)
  3651.                     paramValue = this.$STR("LargeData");
  3652.  
  3653.                 params.push({name: decodeText(paramName), value: decodeText(paramValue)});
  3654.             }
  3655.             else
  3656.             {
  3657.                 var paramName = args[i];
  3658.                 params.push({name: decodeText(paramName), value: ""});
  3659.             }
  3660.         }
  3661.         catch (e)
  3662.         {
  3663.         }
  3664.     }
  3665.  
  3666.     params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; });
  3667.  
  3668.     return params;
  3669. };
  3670.  
  3671. this.reEncodeURL = function(file, text, noLimit)
  3672. {
  3673.     var lines = text.split("\n");
  3674.     var params = this.parseURLEncodedText(lines[lines.length-1], noLimit);
  3675.  
  3676.     var args = [];
  3677.     for (var i = 0; i < params.length; ++i)
  3678.         args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value));
  3679.  
  3680.     var url = file.href;
  3681.     url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&");
  3682.  
  3683.     return url;
  3684. };
  3685.  
  3686. this.getResource = function(aURL)
  3687. {
  3688.     try
  3689.     {
  3690.         var channel=ioService.newChannel(aURL,null,null);
  3691.         var input=channel.open();
  3692.         return FBL.readFromStream(input);
  3693.     }
  3694.     catch (e)
  3695.     {
  3696.     }
  3697. };
  3698.  
  3699. // ************************************************************************************************
  3700. // JSON
  3701.  
  3702. this.parseJSONString = function(jsonString, originURL)
  3703. {
  3704.     var regex = new RegExp(/^\/\*-secure-([\s\S]*)\*\/\s*$/);
  3705.     var matches = regex.exec(jsonString);
  3706.  
  3707.     if (matches)
  3708.     {
  3709.         jsonString = matches[1];
  3710.  
  3711.         if (jsonString[0] == "\\" && jsonString[1] == "n")
  3712.             jsonString = jsonString.substr(2);
  3713.  
  3714.         if (jsonString[jsonString.length-2] == "\\" && jsonString[jsonString.length-1] == "n")
  3715.             jsonString = jsonString.substr(0, jsonString.length-2);
  3716.     }
  3717.  
  3718.     if (jsonString.indexOf("&&&START&&&"))
  3719.     {
  3720.         regex = new RegExp(/&&&START&&& (.+) &&&END&&&/);
  3721.         matches = regex.exec(jsonString);
  3722.         if (matches)
  3723.             jsonString = matches[1];
  3724.     }
  3725.  
  3726.     try
  3727.     {
  3728.         var s = Components.utils.Sandbox(originURL);
  3729.  
  3730.         // throw on the extra parentheses
  3731.         return Components.utils.evalInSandbox("(" + jsonString + ")", s);
  3732.     }
  3733.     catch(e)
  3734.     {
  3735.     }
  3736.  
  3737.     // Let's try to parse it as JSONP.
  3738.     var reJSONP = /^\s*([A-Za-z0-9_.]+\s*(?:\[.*\]|))\s*\(.*\)/;
  3739.     var m = reJSONP.exec(jsonString);
  3740.     if (!m || !m[1])
  3741.         return null;
  3742.  
  3743.     var callbackName = m[1];
  3744.  
  3745.     jsonString = jsonString.replace(callbackName, "callback");
  3746.  
  3747.     try
  3748.     {
  3749.         var s = Components.utils.Sandbox(originURL);
  3750.         s["callback"] = function(object) { return object; };
  3751.         return Components.utils.evalInSandbox(jsonString, s);
  3752.     }
  3753.     catch(ex)
  3754.     {
  3755.     }
  3756.  
  3757.     return null;
  3758. };
  3759.  
  3760. this.parseJSONPString = function(jsonString, originURL)
  3761. {
  3762. }
  3763.  
  3764. // ************************************************************************************************
  3765. // Network
  3766.  
  3767. this.readFromStream = function(stream, charset, noClose)
  3768. {
  3769.     var sis = this.CCSV("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
  3770.     sis.setInputStream(stream);
  3771.  
  3772.     var segments = [];
  3773.     for (var count = stream.available(); count; count = stream.available())
  3774.         segments.push(sis.readBytes(count));
  3775.  
  3776.     if (!noClose)
  3777.         sis.close();
  3778.  
  3779.     var text = segments.join("");
  3780.  
  3781.     try
  3782.     {
  3783.         return this.convertToUnicode(text, charset);
  3784.     }
  3785.     catch (err)
  3786.     {
  3787.     }
  3788.  
  3789.     return text;
  3790. };
  3791.  
  3792. this.readPostTextFromPage = function(url, context)
  3793. {
  3794.     if (url == context.browser.contentWindow.location.href)
  3795.     {
  3796.         try
  3797.         {
  3798.             var webNav = context.browser.webNavigation;
  3799.             var descriptor = this.QI(webNav, Ci.nsIWebPageDescriptor).currentDescriptor;
  3800.             var entry = this.QI(descriptor, Ci.nsISHEntry);
  3801.             if (entry && entry.postData)
  3802.             {
  3803.                 var postStream = this.QI(entry.postData, Ci.nsISeekableStream);
  3804.                 postStream.seek(NS_SEEK_SET, 0);
  3805.  
  3806.                 var charset = context.window.document.characterSet;
  3807.                 return this.readFromStream(postStream, charset, true);
  3808.             }
  3809.          }
  3810.          catch (exc)
  3811.          {
  3812.          }
  3813.      }
  3814. };
  3815.  
  3816. this.readPostTextFromRequest = function(request, context)
  3817. {
  3818.     try
  3819.     {
  3820.         var is = this.QI(request, Ci.nsIUploadChannel).uploadStream;
  3821.         if (is)
  3822.         {
  3823.             var ss = this.QI(is, Ci.nsISeekableStream);
  3824.             var prevOffset;
  3825.             if (ss)
  3826.             {
  3827.                 prevOffset = ss.tell();
  3828.                 ss.seek(NS_SEEK_SET, 0);
  3829.             }
  3830.  
  3831.             // Read data from the stream..
  3832.             var charset = (context && context.window) ? context.window.document.characterSet : null;
  3833.             var text = this.readFromStream(is, charset, true);
  3834.  
  3835.             // Seek locks the file so, seek to the beginning only if necko hasn't read it yet,
  3836.             // since necko doesn't seek to 0 before reading (at lest not till 459384 is fixed).
  3837.             if (ss && prevOffset == 0)
  3838.                 ss.seek(NS_SEEK_SET, 0);
  3839.  
  3840.             return text;
  3841.         }
  3842.     }
  3843.     catch(exc)
  3844.     {
  3845.     }
  3846.  
  3847.     return null;
  3848. };
  3849.  
  3850. this.getInputStreamFromString = function(dataString)
  3851. {
  3852.     var stringStream = this.CCIN("@mozilla.org/io/string-input-stream;1", "nsIStringInputStream");
  3853.  
  3854.     if ("data" in stringStream) // Gecko 1.9 or newer
  3855.         stringStream.data = dataString;
  3856.     else // 1.8 or older
  3857.         stringStream.setData(dataString, dataString.length);
  3858.  
  3859.     return stringStream;
  3860. };
  3861.  
  3862. this.getWindowForRequest = function(request)
  3863. {
  3864.     var webProgress = this.getRequestWebProgress(request);
  3865.     try {
  3866.         if (webProgress)
  3867.             return webProgress.DOMWindow;
  3868.     }
  3869.     catch (ex) {
  3870.     }
  3871.  
  3872.     return null;
  3873. };
  3874.  
  3875. this.getRequestWebProgress = function(request)
  3876. {
  3877.     try
  3878.     {
  3879.         if (request && request.notificationCallbacks)
  3880.             return request.notificationCallbacks.getInterface(Ci.nsIWebProgress);
  3881.     } catch (exc) {}
  3882.  
  3883.     try
  3884.     {
  3885.         if (request && request.loadGroup && request.loadGroup.groupObserver)
  3886.             return request.loadGroup.groupObserver.QueryInterface(Ci.nsIWebProgress);
  3887.     } catch (exc) {}
  3888.  
  3889.     return null;
  3890. };
  3891.  
  3892. /**
  3893.  * Returns <browser> element for specified content window.
  3894.  * @param {Object} win - Content window
  3895.  */
  3896. this.getBrowserForWindow = function(win)
  3897. {
  3898.     var tabBrowser = document.getElementById("content");
  3899.     if (tabBrowser && win.document)
  3900.         return tabBrowser.getBrowserForDocument(win.document);
  3901. };
  3902.  
  3903. // ************************************************************************************************
  3904.  
  3905. this.BaseProgressListener =
  3906. {
  3907.     QueryInterface : function(iid)
  3908.     {
  3909.         if (iid.equals(Ci.nsIWebProgressListener) ||
  3910.             iid.equals(Ci.nsISupportsWeakReference) ||
  3911.             iid.equals(Ci.nsISupports))
  3912.         {
  3913.             return this;
  3914.         }
  3915.  
  3916.         throw Components.results.NS_NOINTERFACE;
  3917.     },
  3918.  
  3919.     stateIsRequest: false,
  3920.     onLocationChange: function() {},
  3921.     onStateChange : function() {},
  3922.     onProgressChange : function() {},
  3923.     onStatusChange : function() {},
  3924.     onSecurityChange : function() {},
  3925.     onLinkIconAvailable : function() {}
  3926. };
  3927.  
  3928. // ************************************************************************************************
  3929. // Network Tracing
  3930.  
  3931. this.getStateDescription = function(flag)
  3932. {
  3933.     var state = [];
  3934.     var nsIWebProgressListener = Ci.nsIWebProgressListener;
  3935.     if (flag & nsIWebProgressListener.STATE_START) state.push("STATE_START");
  3936.     else if (flag & nsIWebProgressListener.STATE_REDIRECTING) state.push("STATE_REDIRECTING");
  3937.     else if (flag & nsIWebProgressListener.STATE_TRANSFERRING) state.push("STATE_TRANSFERRING");
  3938.     else if (flag & nsIWebProgressListener.STATE_NEGOTIATING) state.push("STATE_NEGOTIATING");
  3939.     else if (flag & nsIWebProgressListener.STATE_STOP) state.push("STATE_STOP");
  3940.  
  3941.     if (flag & nsIWebProgressListener.STATE_IS_REQUEST) state.push("STATE_IS_REQUEST");
  3942.     if (flag & nsIWebProgressListener.STATE_IS_DOCUMENT) state.push("STATE_IS_DOCUMENT");
  3943.     if (flag & nsIWebProgressListener.STATE_IS_NETWORK) state.push("STATE_IS_NETWORK");
  3944.     if (flag & nsIWebProgressListener.STATE_IS_WINDOW) state.push("STATE_IS_WINDOW");
  3945.     if (flag & nsIWebProgressListener.STATE_RESTORING) state.push("STATE_RESTORING");
  3946.     if (flag & nsIWebProgressListener.STATE_IS_INSECURE) state.push("STATE_IS_INSECURE");
  3947.     if (flag & nsIWebProgressListener.STATE_IS_BROKEN) state.push("STATE_IS_BROKEN");
  3948.     if (flag & nsIWebProgressListener.STATE_IS_SECURE) state.push("STATE_IS_SECURE");
  3949.     if (flag & nsIWebProgressListener.STATE_SECURE_HIGH) state.push("STATE_SECURE_HIGH");
  3950.     if (flag & nsIWebProgressListener.STATE_SECURE_MED) state.push("STATE_SECURE_MED");
  3951.     if (flag & nsIWebProgressListener.STATE_SECURE_LOW) state.push("STATE_SECURE_LOW");
  3952.  
  3953.     return state.join(", ");
  3954. };
  3955.  
  3956. this.getStatusDescription = function(status)
  3957. {
  3958.     var nsISocketTransport = Ci.nsISocketTransport;
  3959.     var nsITransport = Ci.nsITransport;
  3960.  
  3961.     if (status == nsISocketTransport.STATUS_RESOLVING) return "STATUS_RESOLVING";
  3962.     if (status == nsISocketTransport.STATUS_CONNECTING_TO) return "STATUS_CONNECTING_TO";
  3963.     if (status == nsISocketTransport.STATUS_CONNECTED_TO) return "STATUS_CONNECTED_TO";
  3964.     if (status == nsISocketTransport.STATUS_SENDING_TO) return "STATUS_SENDING_TO";
  3965.     if (status == nsISocketTransport.STATUS_WAITING_FOR) return "STATUS_WAITING_FOR";
  3966.     if (status == nsISocketTransport.STATUS_RECEIVING_FROM) return "STATUS_RECEIVING_FROM";
  3967.     if (status == nsITransport.STATUS_READING) return "STATUS_READING";
  3968.     if (status == nsITransport.STATUS_WRITING) return "STATUS_WRITING";
  3969. };
  3970.  
  3971. this.getLoadFlagsDescription = function(loadFlags)
  3972. {
  3973.     var flags = [];
  3974.     var nsIChannel = Ci.nsIChannel;
  3975.     var nsICachingChannel = Ci.nsICachingChannel;
  3976.  
  3977.     if (loadFlags & nsIChannel.LOAD_DOCUMENT_URI) flags.push("LOAD_DOCUMENT_URI");
  3978.     if (loadFlags & nsIChannel.LOAD_RETARGETED_DOCUMENT_URI) flags.push("LOAD_RETARGETED_DOCUMENT_URI");
  3979.     if (loadFlags & nsIChannel.LOAD_REPLACE) flags.push("LOAD_REPLACE");
  3980.     if (loadFlags & nsIChannel.LOAD_INITIAL_DOCUMENT_URI) flags.push("LOAD_INITIAL_DOCUMENT_URI");
  3981.     if (loadFlags & nsIChannel.LOAD_TARGETED) flags.push("LOAD_TARGETED");
  3982.     if (loadFlags & nsIChannel.LOAD_CALL_CONTENT_SNIFFERS) flags.push("LOAD_CALL_CONTENT_SNIFFERS");
  3983.     if (loadFlags & nsICachingChannel.LOAD_NO_NETWORK_IO) flags.push("LOAD_NO_NETWORK_IO");
  3984.     if (loadFlags & nsICachingChannel.LOAD_CHECK_OFFLINE_CACHE) flags.push("LOAD_CHECK_OFFLINE_CACHE");
  3985.     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE) flags.push("LOAD_BYPASS_LOCAL_CACHE");
  3986.     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) flags.push("LOAD_BYPASS_LOCAL_CACHE_IF_BUSY");
  3987.     if (loadFlags & nsICachingChannel.LOAD_ONLY_FROM_CACHE) flags.push("LOAD_ONLY_FROM_CACHE");
  3988.     if (loadFlags & nsICachingChannel.LOAD_ONLY_IF_MODIFIED) flags.push("LOAD_ONLY_IF_MODIFIED");
  3989.  
  3990.     return flags.join(", ");
  3991. };
  3992.  
  3993. // ************************************************************************************************
  3994. // Programs
  3995.  
  3996. this.launchProgram = function(exePath, args)
  3997. {
  3998.     try {
  3999.         var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  4000.         file.initWithPath(exePath);
  4001.         if (this.getPlatformName() == "Darwin" && file.isDirectory())
  4002.         {
  4003.             args = this.extendArray(["-a", exePath], args);
  4004.             file.initWithPath("/usr/bin/open");
  4005.         }
  4006.         if (!file.exists())
  4007.             return false;
  4008.         var process = this.CCIN("@mozilla.org/process/util;1", "nsIProcess");
  4009.         process.init(file);
  4010.         process.run(false, args, args.length, {});
  4011.         return true;
  4012.     }
  4013.     catch(exc)
  4014.     {
  4015.         this.ERROR(exc);
  4016.     }
  4017.     return false;
  4018. };
  4019.  
  4020. this.getIconURLForFile = function(path)
  4021. {
  4022.     var fileHandler = ioService.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler);
  4023.     try {
  4024.         var file = this.CCIN("@mozilla.org/file/local;1", "nsILocalFile");
  4025.         file.initWithPath(path);
  4026.         if ((this.getPlatformName() == "Darwin") && !file.isDirectory() && (path.indexOf(".app/") != -1))
  4027.         {
  4028.             path = path.substr(0,path.lastIndexOf(".app/")+4);
  4029.             file.initWithPath(path);
  4030.         }
  4031.         return "moz-icon://" + fileHandler.getURLSpecFromFile(file) + "?size=16";
  4032.     }
  4033.     catch(exc)
  4034.     {
  4035.         this.ERROR(exc);
  4036.     }
  4037.     return null;
  4038. }
  4039.  
  4040. this.makeURI = function(urlString)
  4041. {
  4042.     try
  4043.     {
  4044.         if (urlString)
  4045.             return ioService.newURI(urlString, null, null);
  4046.     }
  4047.     catch(exc)
  4048.     {
  4049.         //var explain = {message: "Firebug.lib.makeURI FAILS", url: urlString, exception: exc};
  4050.         // todo convert explain to json and then to data url
  4051.         return false;
  4052.     }
  4053. }
  4054.  
  4055. // ************************************************************************************************
  4056.  
  4057. this.persistObjects = function(panel, panelState)
  4058. {
  4059.     // Persist the location and selection so we can restore them in case of a reload
  4060.     if (panel.location)
  4061.         panelState.persistedLocation = this.persistObject(panel.location, panel.context); // fn(context)->location
  4062.  
  4063.     if (panel.selection)
  4064.         panelState.persistedSelection = this.persistObject(panel.selection, panel.context);
  4065. };
  4066.  
  4067. this.persistObject = function(object, context)
  4068. {
  4069.     var rep = Firebug.getRep(object);
  4070.     return rep ? rep.persistObject(object, context) : null;
  4071. };
  4072.  
  4073. this.restoreLocation =  function(panel, panelState)
  4074. {
  4075.     var restored = false;
  4076.  
  4077.     if (!panel.location && panelState && panelState.persistedLocation)
  4078.     {
  4079.         var location = panelState.persistedLocation(panel.context);
  4080.  
  4081.         if (location)
  4082.         {
  4083.             panel.navigate(location);
  4084.             restored = true;
  4085.         }
  4086.     }
  4087.  
  4088.     if (!panel.location)
  4089.         panel.navigate(null);
  4090.  
  4091.     return restored;
  4092. };
  4093.  
  4094. this.restoreSelection = function(panel, panelState)
  4095. {
  4096.     var needRetry = false;
  4097.  
  4098.     if (!panel.selection && panelState && panelState.persistedSelection)
  4099.     {
  4100.         var selection = panelState.persistedSelection(panel.context);
  4101.         if (selection)
  4102.             panel.select(selection);
  4103.         else
  4104.             needRetry = true;
  4105.     }
  4106.  
  4107.     if (!panel.selection)  // Couldn't restore the selection, so select the default object
  4108.         panel.select(null);
  4109.  
  4110.     if (needRetry)
  4111.     {
  4112.         function overrideDefaultWithPersistedSelection()
  4113.         {
  4114.             if (panel.selection == panel.getDefaultSelection(panel.context) && panelState.persistedSelection)
  4115.             {
  4116.                 var selection = panelState.persistedSelection(panel.context);
  4117.                 if (selection)
  4118.                     panel.select(selection);
  4119.             }
  4120.  
  4121.         }
  4122.  
  4123.         // If we couldn't restore the selection, wait a bit and try again
  4124.         panel.context.setTimeout(overrideDefaultWithPersistedSelection, overrideDefaultsWithPersistedValuesTimeout);
  4125.     }
  4126.  
  4127. };
  4128.  
  4129. this.restoreObjects = function(panel, panelState)
  4130. {
  4131.     this.restoreLocation(panel, panelState);
  4132.     this.restoreSelection(panel, panelState);
  4133. };
  4134.  
  4135. this.getPersistedState = function(context, panelName)
  4136. {
  4137.     if (!context)
  4138.         return null;
  4139.  
  4140.     var persistedState = context.persistedState;
  4141.     if (!persistedState)
  4142.         persistedState = context.persistedState = {};
  4143.  
  4144.     if (!persistedState.panelState)
  4145.         persistedState.panelState = {};
  4146.  
  4147.     var panelState = persistedState.panelState[panelName];
  4148.     if (!panelState)
  4149.         panelState = persistedState.panelState[panelName] = {};
  4150.  
  4151.     return panelState;
  4152. };
  4153.  
  4154. // ************************************************************************************************
  4155.  
  4156. this.ErrorMessage = function(message, href, lineNo, source, category, context, trace, msgId)
  4157. {
  4158.     this.message = message;
  4159.     this.href = href;
  4160.     this.lineNo = lineNo;
  4161.     this.source = source;
  4162.     this.category = category;
  4163.     this.context = context;
  4164.     this.trace = trace;
  4165.     this.msgId = msgId;
  4166. };
  4167.  
  4168. this.ErrorMessage.prototype =
  4169. {
  4170.     getSourceLine: function()
  4171.     {
  4172.         return this.context.sourceCache.getLine(this.href, this.lineNo);
  4173.     },
  4174.  
  4175.     resetSource: function()
  4176.     {
  4177.         if (this.href && this.lineNo)
  4178.             this.source = this.getSourceLine();
  4179.     },
  4180.  
  4181.     correctWithStackTrace: function(trace)
  4182.     {
  4183.         var frame = trace.frames[0];
  4184.         if (frame)
  4185.         {
  4186.             this.href = frame.href;
  4187.             this.lineNo = frame.line;
  4188.             this.trace = trace;
  4189.         }
  4190.     },
  4191.  
  4192.     correctSourcePoint: function(sourceName, lineNumber)
  4193.     {
  4194.         this.href = sourceName;
  4195.         this.lineNo = lineNumber;
  4196.     },
  4197. };
  4198.  
  4199.  
  4200.  
  4201. // ************************************************************************************************
  4202.  
  4203. /**
  4204.  * @class Searches for text in a given node.
  4205.  *
  4206.  * @constructor
  4207.  * @param {Node} rootNode Node to search
  4208.  * @param {Function} rowFinder results filter. On find this method will be called
  4209.  *      with the node containing the matched text as the first parameter. This may
  4210.  *      be undefined to return the node as is.
  4211.  */
  4212. this.TextSearch = function(rootNode, rowFinder)
  4213. {
  4214.     var doc = rootNode.ownerDocument;
  4215.     var count, searchRange, startPt;
  4216.  
  4217.     /**
  4218.      * Find the first result in the node.
  4219.      *
  4220.      * @param {String} text Text to search for
  4221.      * @param {boolean} reverse true to perform a reverse search
  4222.      * @param {boolean} caseSensitive true to perform a case sensitive search
  4223.      */
  4224.     this.find = function(text, reverse, caseSensitive)
  4225.     {
  4226.         this.text = text;
  4227.  
  4228.         finder.findBackwards = !!reverse;
  4229.         finder.caseSensitive = !!caseSensitive;
  4230.  
  4231.         var range = this.range = finder.Find(
  4232.                 text, searchRange,
  4233.                 startPt || searchRange,
  4234.                 searchRange);
  4235.         var match = range ?  range.startContainer : null;
  4236.         return this.currentNode = (rowFinder && match ? rowFinder(match) : match);
  4237.     };
  4238.  
  4239.     /**
  4240.      * Find the next search result
  4241.      *
  4242.      * @param {boolean} wrapAround true to wrap the search if the end of range is reached
  4243.      * @param {boolean} sameNode true to return multiple results from the same text node
  4244.      * @param {boolean} reverse true to search in reverse
  4245.      * @param {boolean} caseSensitive true to perform a case sensitive search
  4246.      */
  4247.     this.findNext = function(wrapAround, sameNode, reverse, caseSensitive)
  4248.     {
  4249.         startPt = undefined;
  4250.  
  4251.         if (sameNode && this.range)
  4252.         {
  4253.             startPt = this.range.cloneRange();
  4254.             if (reverse)
  4255.             {
  4256.                 startPt.setEnd(startPt.startContainer, startPt.startOffset);
  4257.             }
  4258.             else
  4259.             {
  4260.                 startPt.setStart(startPt.startContainer, startPt.startOffset+1);
  4261.             }
  4262.         }
  4263.  
  4264.         if (!startPt)
  4265.         {
  4266.             var curNode = this.currentNode ? this.currentNode : rootNode;
  4267.             startPt = doc.createRange();
  4268.             try
  4269.             {
  4270.                 if (reverse)
  4271.                 {
  4272.                     startPt.setStartBefore(curNode);
  4273.                 }
  4274.                 else
  4275.                 {
  4276.                     startPt.setStartAfter(curNode);
  4277.                 }
  4278.             }
  4279.             catch (e)
  4280.             {
  4281.                 try {
  4282.                     FBTrace.sysout("setStart try\n");
  4283.                     startPt.setStart(curNode);
  4284.                     FBTrace.sysout("setStart success\n");
  4285.                 } catch (exc) {
  4286.                     return;
  4287.                 }
  4288.             }
  4289.         }
  4290.  
  4291.         var match = startPt && this.find(this.text, reverse, caseSensitive);
  4292.         if (!match && wrapAround)
  4293.         {
  4294.             this.reset();
  4295.             return this.find(this.text, reverse, caseSensitive);
  4296.         }
  4297.  
  4298.         return match;
  4299.     };
  4300.  
  4301.     /**
  4302.      * Resets the instance state to the initial state.
  4303.      */
  4304.     this.reset = function()
  4305.     {
  4306.         searchRange = doc.createRange();
  4307.         searchRange.selectNode(rootNode);
  4308.  
  4309.         startPt = searchRange;
  4310.     };
  4311.  
  4312.     this.reset();
  4313. };
  4314.  
  4315. // ************************************************************************************************
  4316.  
  4317. this.SourceBoxTextSearch = function(sourceBox)
  4318. {
  4319.     this.find = function(text, reverse, caseSensitive)
  4320.     {
  4321.         this.text = text;
  4322.  
  4323.         this.re = new FBL.ReversibleRegExp(text);
  4324.  
  4325.         return this.findNext(false, reverse, caseSensitive);
  4326.     };
  4327.  
  4328.     this.findNext = function(wrapAround, reverse, caseSensitive)
  4329.     {
  4330.         var lines = sourceBox.lines;
  4331.         var match = null;
  4332.         for (var iter = new FBL.ReversibleIterator(lines.length, this.mark, reverse); iter.next();)
  4333.         {
  4334.             match = this.re.exec(lines[iter.index], false, caseSensitive);
  4335.             if (match)
  4336.             {
  4337.                 this.mark = iter.index;
  4338.                 return iter.index;
  4339.             }
  4340.         }
  4341.  
  4342.         if (!match && wrapAround)
  4343.         {
  4344.             this.reset();
  4345.             return this.findNext(false, reverse, caseSensitive);
  4346.         }
  4347.  
  4348.         return match;
  4349.     };
  4350.  
  4351.     this.reset = function()
  4352.     {
  4353.         delete this.mark;
  4354.     };
  4355.  
  4356.     this.reset();
  4357. };
  4358. //************************************************************************************************
  4359.  
  4360. this.Continued = function()
  4361. {
  4362.  
  4363. };
  4364.  
  4365. this.Continued.prototype =
  4366. {
  4367.     complete: function()
  4368.     {
  4369.         if (this.callback)
  4370.             this.callback.apply(top, arguments);
  4371.         else
  4372.             this.result = cloneArray(arguments);
  4373.     },
  4374.  
  4375.     wait: function(cb)
  4376.     {
  4377.         if ("result" in this)
  4378.             cb.apply(top, this.result);
  4379.         else
  4380.             this.callback = cb;
  4381.     }
  4382. };
  4383.  
  4384. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4385.  
  4386. this.SourceLink = function(url, line, type, object, instance)
  4387. {
  4388.     this.href = url;
  4389.     this.instance = instance;
  4390.     this.line = line;
  4391.     this.type = type;
  4392.     this.object = object;
  4393. };
  4394.  
  4395. this.SourceLink.prototype =
  4396. {
  4397.     toString: function()
  4398.     {
  4399.         return this.href;
  4400.     },
  4401.     toJSON: function() // until 3.1...
  4402.     {
  4403.         return "{\"href\":\""+this.href+"\", "+
  4404.             (this.line?("\"line\":"+this.line+","):"")+
  4405.             (this.type?(" \"type\":\""+this.type+"\","):"")+
  4406.                     "}";
  4407.     }
  4408.  
  4409. };
  4410.  
  4411. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4412.  
  4413. this.SourceText = function(lines, owner)
  4414. {
  4415.     this.lines = lines;
  4416.     this.owner = owner;
  4417. };
  4418.  
  4419. this.SourceText.getLineAsHTML = function(lineNo)
  4420. {
  4421.     return escapeForSourceLine(this.lines[lineNo-1]);
  4422. };
  4423.  
  4424. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4425.  
  4426. this.StackTrace = function()
  4427. {
  4428.     this.frames = [];
  4429. };
  4430.  
  4431. this.StackTrace.prototype =
  4432. {
  4433.     toString: function()
  4434.     {
  4435.         var trace = "<top>\n";
  4436.         for (var i = 0; i < this.frames.length; i++)
  4437.         {
  4438.             trace += "[" + i + "]"+ this.frames[i]+"\n";
  4439.         }
  4440.         trace += "<bottom>\n";
  4441.         return trace;
  4442.     },
  4443.     reverse: function()
  4444.     {
  4445.         this.frames.reverse();
  4446.         return this;
  4447.     },
  4448.  
  4449.     destroy: function()
  4450.     {
  4451.         for (var i = 0; i < this.frames.length; i++)
  4452.         {
  4453.             this.frames[i].destroy();
  4454.         }
  4455.     }
  4456. };
  4457.  
  4458. this.traceToString = function(trace)                /*@explore*/
  4459. {                                                   /*@explore*/
  4460.     var str = "<top>";                              /*@explore*/
  4461.     for(var i = 0; i < trace.frames.length; i++)    /*@explore*/
  4462.         str += "\n" + trace.frames[i];              /*@explore*/
  4463.     str += "\n<bottom>";                            /*@explore*/
  4464.     return str;                                     /*@explore*/
  4465. }
  4466. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4467.  
  4468. this.StackFrame = function(context, fn, script, href, lineNo, args, pc)
  4469. {
  4470.     this.context = context;
  4471.     this.fn = fn;
  4472.     this.script = script;
  4473.     this.href = href;
  4474.     this.line = lineNo;
  4475.     this.args = args;
  4476.     this.flags = (script?script.flags:null);
  4477.     this.pc = pc;
  4478. };
  4479.  
  4480. this.StackFrame.prototype =
  4481. {
  4482.     toString: function()
  4483.     {
  4484.         // XXXjjb analyze args and fn?
  4485.         if (this.script)
  4486.             return "("+this.flags+")"+this.href+":"+this.script.baseLineNumber+"-"
  4487.                   +(this.script.baseLineNumber+this.script.lineExtent)+"@"+this.line;
  4488.         else
  4489.             return this.href;
  4490.     },
  4491.     destroy: function()
  4492.     {
  4493.         this.script = null;
  4494.         this.fn = null;
  4495.     },
  4496.     signature: function()
  4497.     {
  4498.         return this.script.tag +"." + this.pc;
  4499.     }
  4500. };
  4501. //-----------------------111111----222222-----33---444  1 All 'Not a (' followed by (; 2 All 'Not a )' followed by a ); 3 text between @ and : digits
  4502. var reErrorStackLine = /([^\(]*)\(([^\)]*)\)@(.*):(\d*)/;
  4503. this.parseToStackFrame = function(line) // function name (arg, arg, arg)@fileName:lineNo
  4504. {
  4505.     var m = reErrorStackLine.exec(line);
  4506.     if (m)
  4507.         return new this.StackFrame(null, m[1], null, m[3], m[4], m[2].split(','), 0);
  4508. }
  4509. this.parseToStackTrace = function(stack)
  4510. {
  4511.     var lines = stack.split('\n');
  4512.     var trace = new this.StackTrace();
  4513.     for (var i = 0; i < lines.length; i++)
  4514.     {
  4515.         var frame = this.parseToStackFrame(lines[i]);
  4516.         FBTrace.sysout("parseToStackTrace i "+i+" line:"+lines[i]+ "->frame: "+frame, frame);
  4517.         if (frame)
  4518.             trace.frames.push(frame);
  4519.     }
  4520.     return trace;
  4521. }
  4522.  
  4523. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  4524.  
  4525. this.Property = function(object, name)
  4526. {
  4527.     this.object = object;
  4528.     this.name = name;
  4529.  
  4530.     this.getObject = function()
  4531.     {
  4532.         return object[name];
  4533.     };
  4534. };
  4535.  
  4536. this.ErrorCopy = function(message)
  4537. {
  4538.     this.message = message;
  4539. };
  4540.  
  4541. function EventCopy(event)
  4542. {
  4543.     // Because event objects are destroyed arbitrarily by Gecko, we must make a copy of them to
  4544.     // represent them long term in the inspector.
  4545.     for (var name in event)
  4546.     {
  4547.         try {
  4548.             this[name] = event[name];
  4549.         } catch (exc) { }
  4550.     }
  4551. }
  4552.  
  4553. this.EventCopy = EventCopy;
  4554.  
  4555. // ************************************************************************************************
  4556. // DOM Constants
  4557.  
  4558. this.getDOMMembers = function(object)
  4559. {
  4560.     if (!domMemberCache)
  4561.     {
  4562.         domMemberCache = {};
  4563.  
  4564.         for (var name in domMemberMap)
  4565.         {
  4566.             var builtins = domMemberMap[name];
  4567.             var cache = domMemberCache[name] = {};
  4568.  
  4569.             for (var i = 0; i < builtins.length; ++i)
  4570.                 cache[builtins[i]] = i;
  4571.         }
  4572.     }
  4573.  
  4574.     if (object instanceof Window)
  4575.         { return domMemberCache.Window; }
  4576.     else if (object instanceof Document || object instanceof XMLDocument)
  4577.         { return domMemberCache.Document; }
  4578.     else if (object instanceof Location)
  4579.         { return domMemberCache.Location; }
  4580.     else if (object instanceof HTMLImageElement)
  4581.         { return domMemberCache.HTMLImageElement; }
  4582.     else if (object instanceof HTMLAnchorElement)
  4583.         { return domMemberCache.HTMLAnchorElement; }
  4584.     else if (object instanceof HTMLInputElement)
  4585.         { return domMemberCache.HTMLInputElement; }
  4586.     else if (object instanceof HTMLButtonElement)
  4587.         { return domMemberCache.HTMLButtonElement; }
  4588.     else if (object instanceof HTMLFormElement)
  4589.         { return domMemberCache.HTMLFormElement; }
  4590.     else if (object instanceof HTMLBodyElement)
  4591.         { return domMemberCache.HTMLBodyElement; }
  4592.     else if (object instanceof HTMLHtmlElement)
  4593.         { return domMemberCache.HTMLHtmlElement; }
  4594.     else if (object instanceof HTMLScriptElement)
  4595.         { return domMemberCache.HTMLScriptElement; }
  4596.     else if (object instanceof HTMLTableElement)
  4597.         { return domMemberCache.HTMLTableElement; }
  4598.     else if (object instanceof HTMLTableRowElement)
  4599.         { return domMemberCache.HTMLTableRowElement; }
  4600.     else if (object instanceof HTMLTableCellElement)
  4601.         { return domMemberCache.HTMLTableCellElement; }
  4602.     else if (object instanceof HTMLIFrameElement)
  4603.         { return domMemberCache.HTMLIFrameElement; }
  4604.     else if (object instanceof SVGSVGElement)
  4605.         { return domMemberCache.SVGSVGElement; }
  4606.     else if (object instanceof SVGElement)
  4607.         { return domMemberCache.SVGElement; }
  4608.     else if (object instanceof Element)
  4609.         { return domMemberCache.Element; }
  4610.     else if (object instanceof Text || object instanceof CDATASection)
  4611.         { return domMemberCache.Text; }
  4612.     else if (object instanceof Attr)
  4613.         { return domMemberCache.Attr; }
  4614.     else if (object instanceof Node)
  4615.         { return domMemberCache.Node; }
  4616.     else if (object instanceof Event || object instanceof EventCopy)
  4617.         { return domMemberCache.Event; }
  4618.     else
  4619.         return {};
  4620. };
  4621.  
  4622. this.isDOMMember = function(object, propName)
  4623. {
  4624.     var members = this.getDOMMembers(object);
  4625.     return members && propName in members;
  4626. };
  4627.  
  4628. var domMemberCache = null;
  4629. var domMemberMap = {};
  4630.  
  4631. domMemberMap.Window =
  4632. [
  4633.     "document",
  4634.     "frameElement",
  4635.  
  4636.     "innerWidth",
  4637.     "innerHeight",
  4638.     "outerWidth",
  4639.     "outerHeight",
  4640.     "screenX",
  4641.     "screenY",
  4642.     "pageXOffset",
  4643.     "pageYOffset",
  4644.     "scrollX",
  4645.     "scrollY",
  4646.     "scrollMaxX",
  4647.     "scrollMaxY",
  4648.  
  4649.     "status",
  4650.     "defaultStatus",
  4651.  
  4652.     "parent",
  4653.     "opener",
  4654.     "top",
  4655.     "window",
  4656.     "content",
  4657.     "self",
  4658.  
  4659.     "location",
  4660.     "history",
  4661.     "frames",
  4662.     "navigator",
  4663.     "screen",
  4664.     "menubar",
  4665.     "toolbar",
  4666.     "locationbar",
  4667.     "personalbar",
  4668.     "statusbar",
  4669.     "directories",
  4670.     "scrollbars",
  4671.     "fullScreen",
  4672.     "netscape",
  4673.     "java",
  4674.     "console",
  4675.     "Components",
  4676.     "controllers",
  4677.     "closed",
  4678.     "crypto",
  4679.     "pkcs11",
  4680.  
  4681.     "name",
  4682.     "property",
  4683.     "length",
  4684.  
  4685.     "sessionStorage",
  4686.     "globalStorage",
  4687.  
  4688.     "setTimeout",
  4689.     "setInterval",
  4690.     "clearTimeout",
  4691.     "clearInterval",
  4692.     "addEventListener",
  4693.     "removeEventListener",
  4694.     "dispatchEvent",
  4695.     "getComputedStyle",
  4696.     "captureEvents",
  4697.     "releaseEvents",
  4698.     "routeEvent",
  4699.     "enableExternalCapture",
  4700.     "disableExternalCapture",
  4701.     "moveTo",
  4702.     "moveBy",
  4703.     "resizeTo",
  4704.     "resizeBy",
  4705.     "scroll",
  4706.     "scrollTo",
  4707.     "scrollBy",
  4708.     "scrollByLines",
  4709.     "scrollByPages",
  4710.     "sizeToContent",
  4711.     "setResizable",
  4712.     "getSelection",
  4713.     "open",
  4714.     "openDialog",
  4715.     "close",
  4716.     "alert",
  4717.     "confirm",
  4718.     "prompt",
  4719.     "dump",
  4720.     "focus",
  4721.     "blur",
  4722.     "find",
  4723.     "back",
  4724.     "forward",
  4725.     "home",
  4726.     "stop",
  4727.     "print",
  4728.     "atob",
  4729.     "btoa",
  4730.     "updateCommands",
  4731.     "XPCNativeWrapper",
  4732.     "GeckoActiveXObject",
  4733.     "applicationCache",      // FF3
  4734.     "GetWeakReference", // Gecko
  4735.     "XPCSafeJSObjectWrapper", // Gecko
  4736.     "postMessage",
  4737.     "localStorage",  // FF3.5
  4738.     "showModalDialog", // FF 3.0, MS IE4
  4739. ];
  4740.  
  4741. domMemberMap.Location =
  4742. [
  4743.     "href",
  4744.     "protocol",
  4745.     "host",
  4746.     "hostname",
  4747.     "port",
  4748.     "pathname",
  4749.     "search",
  4750.     "hash",
  4751.  
  4752.     "assign",
  4753.     "reload",
  4754.     "replace"
  4755. ];
  4756.  
  4757. domMemberMap.Node =
  4758. [
  4759.     "id",
  4760.     "className",
  4761.  
  4762.     "nodeType",
  4763.     "tagName",
  4764.     "nodeName",
  4765.     "localName",
  4766.     "prefix",
  4767.     "namespaceURI",
  4768.     "nodeValue",
  4769.  
  4770.     "ownerDocument",
  4771.     "parentNode",
  4772.     "offsetParent",
  4773.     "nextSibling",
  4774.     "previousSibling",
  4775.     "firstChild",
  4776.     "lastChild",
  4777.     "childNodes",
  4778.     "attributes",
  4779.  
  4780.     "dir",
  4781.     "baseURI",
  4782.     "textContent",
  4783.     "innerHTML",
  4784.  
  4785.     "addEventListener",
  4786.     "removeEventListener",
  4787.     "dispatchEvent",
  4788.     "cloneNode",
  4789.     "appendChild",
  4790.     "insertBefore",
  4791.     "replaceChild",
  4792.     "removeChild",
  4793.     "compareDocumentPosition",
  4794.     "hasAttributes",
  4795.     "hasChildNodes",
  4796.     "lookupNamespaceURI",
  4797.     "lookupPrefix",
  4798.     "normalize",
  4799.     "isDefaultNamespace",
  4800.     "isEqualNode",
  4801.     "isSameNode",
  4802.     "isSupported",
  4803.     "getFeature",
  4804.     "getUserData",
  4805.     "setUserData"
  4806. ];
  4807.  
  4808. domMemberMap.Document = extendArray(domMemberMap.Node,
  4809. [
  4810.     "documentElement",
  4811.     "body",
  4812.     "title",
  4813.     "location",
  4814.     "referrer",
  4815.     "cookie",
  4816.     "contentType",
  4817.     "lastModified",
  4818.     "characterSet",
  4819.     "inputEncoding",
  4820.     "xmlEncoding",
  4821.     "xmlStandalone",
  4822.     "xmlVersion",
  4823.     "strictErrorChecking",
  4824.     "documentURI",
  4825.     "URL",
  4826.  
  4827.     "defaultView",
  4828.     "doctype",
  4829.     "implementation",
  4830.     "styleSheets",
  4831.     "images",
  4832.     "links",
  4833.     "forms",
  4834.     "anchors",
  4835.     "embeds",
  4836.     "plugins",
  4837.     "applets",
  4838.  
  4839.     "width",
  4840.     "height",
  4841.  
  4842.     "designMode",
  4843.     "compatMode",
  4844.     "async",
  4845.     "preferredStylesheetSet",
  4846.  
  4847.     "alinkColor",
  4848.     "linkColor",
  4849.     "vlinkColor",
  4850.     "bgColor",
  4851.     "fgColor",
  4852.     "domain",
  4853.  
  4854.     "addEventListener",
  4855.     "removeEventListener",
  4856.     "dispatchEvent",
  4857.     "captureEvents",
  4858.     "releaseEvents",
  4859.     "routeEvent",
  4860.     "clear",
  4861.     "open",
  4862.     "close",
  4863.     "execCommand",
  4864.     "execCommandShowHelp",
  4865.     "getElementsByName",
  4866.     "getSelection",
  4867.     "queryCommandEnabled",
  4868.     "queryCommandIndeterm",
  4869.     "queryCommandState",
  4870.     "queryCommandSupported",
  4871.     "queryCommandText",
  4872.     "queryCommandValue",
  4873.     "write",
  4874.     "writeln",
  4875.     "adoptNode",
  4876.     "appendChild",
  4877.     "removeChild",
  4878.     "renameNode",
  4879.     "cloneNode",
  4880.     "compareDocumentPosition",
  4881.     "createAttribute",
  4882.     "createAttributeNS",
  4883.     "createCDATASection",
  4884.     "createComment",
  4885.     "createDocumentFragment",
  4886.     "createElement",
  4887.     "createElementNS",
  4888.     "createEntityReference",
  4889.     "createEvent",
  4890.     "createExpression",
  4891.     "createNSResolver",
  4892.     "createNodeIterator",
  4893.     "createProcessingInstruction",
  4894.     "createRange",
  4895.     "createTextNode",
  4896.     "createTreeWalker",
  4897.     "domConfig",
  4898.     "evaluate",
  4899.     "evaluateFIXptr",
  4900.     "evaluateXPointer",
  4901.     "getAnonymousElementByAttribute",
  4902.     "getAnonymousNodes",
  4903.     "addBinding",
  4904.     "removeBinding",
  4905.     "getBindingParent",
  4906.     "getBoxObjectFor",
  4907.     "setBoxObjectFor",
  4908.     "getElementById",
  4909.     "getElementsByTagName",
  4910.     "getElementsByTagNameNS",
  4911.     "hasAttributes",
  4912.     "hasChildNodes",
  4913.     "importNode",
  4914.     "insertBefore",
  4915.     "isDefaultNamespace",
  4916.     "isEqualNode",
  4917.     "isSameNode",
  4918.     "isSupported",
  4919.     "load",
  4920.     "loadBindingDocument",
  4921.     "lookupNamespaceURI",
  4922.     "lookupPrefix",
  4923.     "normalize",
  4924.     "normalizeDocument",
  4925.     "getFeature",
  4926.     "getUserData",
  4927.     "setUserData"
  4928. ]);
  4929.  
  4930. domMemberMap.Element = extendArray(domMemberMap.Node,
  4931. [
  4932.     "clientWidth",
  4933.     "clientHeight",
  4934.     "offsetLeft",
  4935.     "offsetTop",
  4936.     "offsetWidth",
  4937.     "offsetHeight",
  4938.     "scrollLeft",
  4939.     "scrollTop",
  4940.     "scrollWidth",
  4941.     "scrollHeight",
  4942.  
  4943.     "style",
  4944.  
  4945.     "tabIndex",
  4946.     "title",
  4947.     "lang",
  4948.     "align",
  4949.     "spellcheck",
  4950.  
  4951.     "addEventListener",
  4952.     "removeEventListener",
  4953.     "dispatchEvent",
  4954.     "focus",
  4955.     "blur",
  4956.     "cloneNode",
  4957.     "appendChild",
  4958.     "insertBefore",
  4959.     "replaceChild",
  4960.     "removeChild",
  4961.     "compareDocumentPosition",
  4962.     "getElementsByTagName",
  4963.     "getElementsByTagNameNS",
  4964.     "getAttribute",
  4965.     "getAttributeNS",
  4966.     "getAttributeNode",
  4967.     "getAttributeNodeNS",
  4968.     "setAttribute",
  4969.     "setAttributeNS",
  4970.     "setAttributeNode",
  4971.     "setAttributeNodeNS",
  4972.     "removeAttribute",
  4973.     "removeAttributeNS",
  4974.     "removeAttributeNode",
  4975.     "hasAttribute",
  4976.     "hasAttributeNS",
  4977.     "hasAttributes",
  4978.     "hasChildNodes",
  4979.     "lookupNamespaceURI",
  4980.     "lookupPrefix",
  4981.     "normalize",
  4982.     "isDefaultNamespace",
  4983.     "isEqualNode",
  4984.     "isSameNode",
  4985.     "isSupported",
  4986.     "getFeature",
  4987.     "getUserData",
  4988.     "setUserData"
  4989. ]);
  4990.  
  4991. domMemberMap.SVGElement = extendArray(domMemberMap.Element,
  4992. [
  4993.     "x",
  4994.     "y",
  4995.     "width",
  4996.     "height",
  4997.     "rx",
  4998.     "ry",
  4999.     "transform",
  5000.     "href",
  5001.  
  5002.     "ownerSVGElement",
  5003.     "viewportElement",
  5004.     "farthestViewportElement",
  5005.     "nearestViewportElement",
  5006.  
  5007.     "getBBox",
  5008.     "getCTM",
  5009.     "getScreenCTM",
  5010.     "getTransformToElement",
  5011.     "getPresentationAttribute",
  5012.     "preserveAspectRatio"
  5013. ]);
  5014.  
  5015. domMemberMap.SVGSVGElement = extendArray(domMemberMap.Element,
  5016. [
  5017.     "x",
  5018.     "y",
  5019.     "width",
  5020.     "height",
  5021.     "rx",
  5022.     "ry",
  5023.     "transform",
  5024.  
  5025.     "viewBox",
  5026.     "viewport",
  5027.     "currentView",
  5028.     "useCurrentView",
  5029.     "pixelUnitToMillimeterX",
  5030.     "pixelUnitToMillimeterY",
  5031.     "screenPixelToMillimeterX",
  5032.     "screenPixelToMillimeterY",
  5033.     "currentScale",
  5034.     "currentTranslate",
  5035.     "zoomAndPan",
  5036.  
  5037.     "ownerSVGElement",
  5038.     "viewportElement",
  5039.     "farthestViewportElement",
  5040.     "nearestViewportElement",
  5041.     "contentScriptType",
  5042.     "contentStyleType",
  5043.  
  5044.     "getBBox",
  5045.     "getCTM",
  5046.     "getScreenCTM",
  5047.     "getTransformToElement",
  5048.     "getEnclosureList",
  5049.     "getIntersectionList",
  5050.     "getViewboxToViewportTransform",
  5051.     "getPresentationAttribute",
  5052.     "getElementById",
  5053.     "checkEnclosure",
  5054.     "checkIntersection",
  5055.     "createSVGAngle",
  5056.     "createSVGLength",
  5057.     "createSVGMatrix",
  5058.     "createSVGNumber",
  5059.     "createSVGPoint",
  5060.     "createSVGRect",
  5061.     "createSVGString",
  5062.     "createSVGTransform",
  5063.     "createSVGTransformFromMatrix",
  5064.     "deSelectAll",
  5065.     "preserveAspectRatio",
  5066.     "forceRedraw",
  5067.     "suspendRedraw",
  5068.     "unsuspendRedraw",
  5069.     "unsuspendRedrawAll",
  5070.     "getCurrentTime",
  5071.     "setCurrentTime",
  5072.     "animationsPaused",
  5073.     "pauseAnimations",
  5074.     "unpauseAnimations"
  5075. ]);
  5076.  
  5077. domMemberMap.HTMLImageElement = extendArray(domMemberMap.Element,
  5078. [
  5079.     "src",
  5080.     "naturalWidth",
  5081.     "naturalHeight",
  5082.     "width",
  5083.     "height",
  5084.     "x",
  5085.     "y",
  5086.     "name",
  5087.     "alt",
  5088.     "longDesc",
  5089.     "lowsrc",
  5090.     "border",
  5091.     "complete",
  5092.     "hspace",
  5093.     "vspace",
  5094.     "isMap",
  5095.     "useMap",
  5096. ]);
  5097.  
  5098. domMemberMap.HTMLAnchorElement = extendArray(domMemberMap.Element,
  5099. [
  5100.     "name",
  5101.     "target",
  5102.     "accessKey",
  5103.     "href",
  5104.     "protocol",
  5105.     "host",
  5106.     "hostname",
  5107.     "port",
  5108.     "pathname",
  5109.     "search",
  5110.     "hash",
  5111.     "hreflang",
  5112.     "coords",
  5113.     "shape",
  5114.     "text",
  5115.     "type",
  5116.     "rel",
  5117.     "rev",
  5118.     "charset"
  5119. ]);
  5120.  
  5121. domMemberMap.HTMLIFrameElement = extendArray(domMemberMap.Element,
  5122. [
  5123.     "contentDocument",
  5124.     "contentWindow",
  5125.     "frameBorder",
  5126.     "height",
  5127.     "longDesc",
  5128.     "marginHeight",
  5129.     "marginWidth",
  5130.     "name",
  5131.     "scrolling",
  5132.     "src",
  5133.     "width"
  5134. ]);
  5135.  
  5136. domMemberMap.HTMLTableElement = extendArray(domMemberMap.Element,
  5137. [
  5138.     "bgColor",
  5139.     "border",
  5140.     "caption",
  5141.     "cellPadding",
  5142.     "cellSpacing",
  5143.     "frame",
  5144.     "rows",
  5145.     "rules",
  5146.     "summary",
  5147.     "tBodies",
  5148.     "tFoot",
  5149.     "tHead",
  5150.     "width",
  5151.  
  5152.     "createCaption",
  5153.     "createTFoot",
  5154.     "createTHead",
  5155.     "deleteCaption",
  5156.     "deleteRow",
  5157.     "deleteTFoot",
  5158.     "deleteTHead",
  5159.     "insertRow"
  5160. ]);
  5161.  
  5162. domMemberMap.HTMLTableRowElement = extendArray(domMemberMap.Element,
  5163. [
  5164.     "bgColor",
  5165.     "cells",
  5166.     "ch",
  5167.     "chOff",
  5168.     "rowIndex",
  5169.     "sectionRowIndex",
  5170.     "vAlign",
  5171.  
  5172.     "deleteCell",
  5173.     "insertCell"
  5174. ]);
  5175.  
  5176. domMemberMap.HTMLTableCellElement = extendArray(domMemberMap.Element,
  5177. [
  5178.     "abbr",
  5179.     "axis",
  5180.     "bgColor",
  5181.     "cellIndex",
  5182.     "ch",
  5183.     "chOff",
  5184.     "colSpan",
  5185.     "headers",
  5186.     "height",
  5187.     "noWrap",
  5188.     "rowSpan",
  5189.     "scope",
  5190.     "vAlign",
  5191.     "width"
  5192.  
  5193. ]);
  5194.  
  5195. domMemberMap.HTMLScriptElement = extendArray(domMemberMap.Element,
  5196. [
  5197.     "src"
  5198. ]);
  5199.  
  5200. domMemberMap.HTMLButtonElement = extendArray(domMemberMap.Element,
  5201. [
  5202.     "accessKey",
  5203.     "disabled",
  5204.     "form",
  5205.     "name",
  5206.     "type",
  5207.     "value",
  5208.  
  5209.     "click"
  5210. ]);
  5211.  
  5212. domMemberMap.HTMLInputElement = extendArray(domMemberMap.Element,
  5213. [
  5214.     "type",
  5215.     "value",
  5216.     "checked",
  5217.     "accept",
  5218.     "accessKey",
  5219.     "alt",
  5220.     "controllers",
  5221.     "defaultChecked",
  5222.     "defaultValue",
  5223.     "disabled",
  5224.     "form",
  5225.     "maxLength",
  5226.     "name",
  5227.     "readOnly",
  5228.     "selectionEnd",
  5229.     "selectionStart",
  5230.     "size",
  5231.     "src",
  5232.     "textLength",
  5233.     "useMap",
  5234.  
  5235.     "click",
  5236.     "select",
  5237.     "setSelectionRange"
  5238. ]);
  5239.  
  5240. domMemberMap.HTMLFormElement = extendArray(domMemberMap.Element,
  5241. [
  5242.     "acceptCharset",
  5243.     "action",
  5244.     "author",
  5245.     "elements",
  5246.     "encoding",
  5247.     "enctype",
  5248.     "entry_id",
  5249.     "length",
  5250.     "method",
  5251.     "name",
  5252.     "post",
  5253.     "target",
  5254.     "text",
  5255.     "url",
  5256.  
  5257.     "reset",
  5258.     "submit"
  5259. ]);
  5260.  
  5261. domMemberMap.HTMLBodyElement = extendArray(domMemberMap.Element,
  5262. [
  5263.     "aLink",
  5264.     "background",
  5265.     "bgColor",
  5266.     "link",
  5267.     "text",
  5268.     "vLink"
  5269. ]);
  5270.  
  5271. domMemberMap.HTMLHtmlElement = extendArray(domMemberMap.Element,
  5272. [
  5273.     "version"
  5274. ]);
  5275.  
  5276. domMemberMap.Text = extendArray(domMemberMap.Node,
  5277. [
  5278.     "data",
  5279.     "length",
  5280.  
  5281.     "appendData",
  5282.     "deleteData",
  5283.     "insertData",
  5284.     "replaceData",
  5285.     "splitText",
  5286.     "substringData"
  5287. ]);
  5288.  
  5289. domMemberMap.Attr = extendArray(domMemberMap.Node,
  5290. [
  5291.     "name",
  5292.     "value",
  5293.     "specified",
  5294.     "ownerElement"
  5295. ]);
  5296.  
  5297. domMemberMap.Event =
  5298. [
  5299.     "type",
  5300.     "target",
  5301.     "currentTarget",
  5302.     "originalTarget",
  5303.     "explicitOriginalTarget",
  5304.     "relatedTarget",
  5305.     "rangeParent",
  5306.     "rangeOffset",
  5307.     "view",
  5308.  
  5309.     "keyCode",
  5310.     "charCode",
  5311.     "screenX",
  5312.     "screenY",
  5313.     "clientX",
  5314.     "clientY",
  5315.     "layerX",
  5316.     "layerY",
  5317.     "pageX",
  5318.     "pageY",
  5319.  
  5320.     "detail",
  5321.     "button",
  5322.     "which",
  5323.     "ctrlKey",
  5324.     "shiftKey",
  5325.     "altKey",
  5326.     "metaKey",
  5327.  
  5328.     "eventPhase",
  5329.     "timeStamp",
  5330.     "bubbles",
  5331.     "cancelable",
  5332.     "cancelBubble",
  5333.  
  5334.     "isTrusted",
  5335.     "isChar",
  5336.  
  5337.     "getPreventDefault",
  5338.     "initEvent",
  5339.     "initMouseEvent",
  5340.     "initKeyEvent",
  5341.     "initUIEvent",
  5342.     "preventBubble",
  5343.     "preventCapture",
  5344.     "preventDefault",
  5345.     "stopPropagation"
  5346. ];
  5347.  
  5348. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  5349.  
  5350. this.domConstantMap =
  5351. {
  5352.     "ELEMENT_NODE": 1,
  5353.     "ATTRIBUTE_NODE": 1,
  5354.     "TEXT_NODE": 1,
  5355.     "CDATA_SECTION_NODE": 1,
  5356.     "ENTITY_REFERENCE_NODE": 1,
  5357.     "ENTITY_NODE": 1,
  5358.     "PROCESSING_INSTRUCTION_NODE": 1,
  5359.     "COMMENT_NODE": 1,
  5360.     "DOCUMENT_NODE": 1,
  5361.     "DOCUMENT_TYPE_NODE": 1,
  5362.     "DOCUMENT_FRAGMENT_NODE": 1,
  5363.     "NOTATION_NODE": 1,
  5364.  
  5365.     "DOCUMENT_POSITION_DISCONNECTED": 1,
  5366.     "DOCUMENT_POSITION_PRECEDING": 1,
  5367.     "DOCUMENT_POSITION_FOLLOWING": 1,
  5368.     "DOCUMENT_POSITION_CONTAINS": 1,
  5369.     "DOCUMENT_POSITION_CONTAINED_BY": 1,
  5370.     "DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC": 1,
  5371.  
  5372.     "UNKNOWN_RULE": 1,
  5373.     "STYLE_RULE": 1,
  5374.     "CHARSET_RULE": 1,
  5375.     "IMPORT_RULE": 1,
  5376.     "MEDIA_RULE": 1,
  5377.     "FONT_FACE_RULE": 1,
  5378.     "PAGE_RULE": 1,
  5379.  
  5380.     "CAPTURING_PHASE": 1,
  5381.     "AT_TARGET": 1,
  5382.     "BUBBLING_PHASE": 1,
  5383.  
  5384.     "SCROLL_PAGE_UP": 1,
  5385.     "SCROLL_PAGE_DOWN": 1,
  5386.  
  5387.     "MOUSEUP": 1,
  5388.     "MOUSEDOWN": 1,
  5389.     "MOUSEOVER": 1,
  5390.     "MOUSEOUT": 1,
  5391.     "MOUSEMOVE": 1,
  5392.     "MOUSEDRAG": 1,
  5393.     "CLICK": 1,
  5394.     "DBLCLICK": 1,
  5395.     "KEYDOWN": 1,
  5396.     "KEYUP": 1,
  5397.     "KEYPRESS": 1,
  5398.     "DRAGDROP": 1,
  5399.     "FOCUS": 1,
  5400.     "BLUR": 1,
  5401.     "SELECT": 1,
  5402.     "CHANGE": 1,
  5403.     "RESET": 1,
  5404.     "SUBMIT": 1,
  5405.     "SCROLL": 1,
  5406.     "LOAD": 1,
  5407.     "UNLOAD": 1,
  5408.     "XFER_DONE": 1,
  5409.     "ABORT": 1,
  5410.     "ERROR": 1,
  5411.     "LOCATE": 1,
  5412.     "MOVE": 1,
  5413.     "RESIZE": 1,
  5414.     "FORWARD": 1,
  5415.     "HELP": 1,
  5416.     "BACK": 1,
  5417.     "TEXT": 1,
  5418.  
  5419.     "ALT_MASK": 1,
  5420.     "CONTROL_MASK": 1,
  5421.     "SHIFT_MASK": 1,
  5422.     "META_MASK": 1,
  5423.  
  5424.     "DOM_VK_TAB": 1,
  5425.     "DOM_VK_PAGE_UP": 1,
  5426.     "DOM_VK_PAGE_DOWN": 1,
  5427.     "DOM_VK_UP": 1,
  5428.     "DOM_VK_DOWN": 1,
  5429.     "DOM_VK_LEFT": 1,
  5430.     "DOM_VK_RIGHT": 1,
  5431.     "DOM_VK_CANCEL": 1,
  5432.     "DOM_VK_HELP": 1,
  5433.     "DOM_VK_BACK_SPACE": 1,
  5434.     "DOM_VK_CLEAR": 1,
  5435.     "DOM_VK_RETURN": 1,
  5436.     "DOM_VK_ENTER": 1,
  5437.     "DOM_VK_SHIFT": 1,
  5438.     "DOM_VK_CONTROL": 1,
  5439.     "DOM_VK_ALT": 1,
  5440.     "DOM_VK_PAUSE": 1,
  5441.     "DOM_VK_CAPS_LOCK": 1,
  5442.     "DOM_VK_ESCAPE": 1,
  5443.     "DOM_VK_SPACE": 1,
  5444.     "DOM_VK_END": 1,
  5445.     "DOM_VK_HOME": 1,
  5446.     "DOM_VK_PRINTSCREEN": 1,
  5447.     "DOM_VK_INSERT": 1,
  5448.     "DOM_VK_DELETE": 1,
  5449.     "DOM_VK_0": 1,
  5450.     "DOM_VK_1": 1,
  5451.     "DOM_VK_2": 1,
  5452.     "DOM_VK_3": 1,
  5453.     "DOM_VK_4": 1,
  5454.     "DOM_VK_5": 1,
  5455.     "DOM_VK_6": 1,
  5456.     "DOM_VK_7": 1,
  5457.     "DOM_VK_8": 1,
  5458.     "DOM_VK_9": 1,
  5459.     "DOM_VK_SEMICOLON": 1,
  5460.     "DOM_VK_EQUALS": 1,
  5461.     "DOM_VK_A": 1,
  5462.     "DOM_VK_B": 1,
  5463.     "DOM_VK_C": 1,
  5464.     "DOM_VK_D": 1,
  5465.     "DOM_VK_E": 1,
  5466.     "DOM_VK_F": 1,
  5467.     "DOM_VK_G": 1,
  5468.     "DOM_VK_H": 1,
  5469.     "DOM_VK_I": 1,
  5470.     "DOM_VK_J": 1,
  5471.     "DOM_VK_K": 1,
  5472.     "DOM_VK_L": 1,
  5473.     "DOM_VK_M": 1,
  5474.     "DOM_VK_N": 1,
  5475.     "DOM_VK_O": 1,
  5476.     "DOM_VK_P": 1,
  5477.     "DOM_VK_Q": 1,
  5478.     "DOM_VK_R": 1,
  5479.     "DOM_VK_S": 1,
  5480.     "DOM_VK_T": 1,
  5481.     "DOM_VK_U": 1,
  5482.     "DOM_VK_V": 1,
  5483.     "DOM_VK_W": 1,
  5484.     "DOM_VK_X": 1,
  5485.     "DOM_VK_Y": 1,
  5486.     "DOM_VK_Z": 1,
  5487.     "DOM_VK_CONTEXT_MENU": 1,
  5488.     "DOM_VK_NUMPAD0": 1,
  5489.     "DOM_VK_NUMPAD1": 1,
  5490.     "DOM_VK_NUMPAD2": 1,
  5491.     "DOM_VK_NUMPAD3": 1,
  5492.     "DOM_VK_NUMPAD4": 1,
  5493.     "DOM_VK_NUMPAD5": 1,
  5494.     "DOM_VK_NUMPAD6": 1,
  5495.     "DOM_VK_NUMPAD7": 1,
  5496.     "DOM_VK_NUMPAD8": 1,
  5497.     "DOM_VK_NUMPAD9": 1,
  5498.     "DOM_VK_MULTIPLY": 1,
  5499.     "DOM_VK_ADD": 1,
  5500.     "DOM_VK_SEPARATOR": 1,
  5501.     "DOM_VK_SUBTRACT": 1,
  5502.     "DOM_VK_DECIMAL": 1,
  5503.     "DOM_VK_DIVIDE": 1,
  5504.     "DOM_VK_F1": 1,
  5505.     "DOM_VK_F2": 1,
  5506.     "DOM_VK_F3": 1,
  5507.     "DOM_VK_F4": 1,
  5508.     "DOM_VK_F5": 1,
  5509.     "DOM_VK_F6": 1,
  5510.     "DOM_VK_F7": 1,
  5511.     "DOM_VK_F8": 1,
  5512.     "DOM_VK_F9": 1,
  5513.     "DOM_VK_F10": 1,
  5514.     "DOM_VK_F11": 1,
  5515.     "DOM_VK_F12": 1,
  5516.     "DOM_VK_F13": 1,
  5517.     "DOM_VK_F14": 1,
  5518.     "DOM_VK_F15": 1,
  5519.     "DOM_VK_F16": 1,
  5520.     "DOM_VK_F17": 1,
  5521.     "DOM_VK_F18": 1,
  5522.     "DOM_VK_F19": 1,
  5523.     "DOM_VK_F20": 1,
  5524.     "DOM_VK_F21": 1,
  5525.     "DOM_VK_F22": 1,
  5526.     "DOM_VK_F23": 1,
  5527.     "DOM_VK_F24": 1,
  5528.     "DOM_VK_NUM_LOCK": 1,
  5529.     "DOM_VK_SCROLL_LOCK": 1,
  5530.     "DOM_VK_COMMA": 1,
  5531.     "DOM_VK_PERIOD": 1,
  5532.     "DOM_VK_SLASH": 1,
  5533.     "DOM_VK_BACK_QUOTE": 1,
  5534.     "DOM_VK_OPEN_BRACKET": 1,
  5535.     "DOM_VK_BACK_SLASH": 1,
  5536.     "DOM_VK_CLOSE_BRACKET": 1,
  5537.     "DOM_VK_QUOTE": 1,
  5538.     "DOM_VK_META": 1,
  5539.  
  5540.     "SVG_ZOOMANDPAN_DISABLE": 1,
  5541.     "SVG_ZOOMANDPAN_MAGNIFY": 1,
  5542.     "SVG_ZOOMANDPAN_UNKNOWN": 1
  5543. };
  5544.  
  5545. this.cssInfo =
  5546. {
  5547.     "background": ["bgRepeat", "bgAttachment", "bgPosition", "color", "systemColor", "none"],
  5548.     "background-attachment": ["bgAttachment"],
  5549.     "background-color": ["color", "systemColor"],
  5550.     "background-image": ["none"],
  5551.     "background-position": ["bgPosition"],
  5552.     "background-repeat": ["bgRepeat"],
  5553.  
  5554.     "border": ["borderStyle", "thickness", "color", "systemColor", "none"],
  5555.     "border-top": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5556.     "border-right": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5557.     "border-bottom": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5558.     "border-left": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5559.     "border-collapse": ["borderCollapse"],
  5560.     "border-color": ["color", "systemColor"],
  5561.     "border-top-color": ["color", "systemColor"],
  5562.     "border-right-color": ["color", "systemColor"],
  5563.     "border-bottom-color": ["color", "systemColor"],
  5564.     "border-left-color": ["color", "systemColor"],
  5565.     "border-spacing": [],
  5566.     "border-style": ["borderStyle"],
  5567.     "border-top-style": ["borderStyle"],
  5568.     "border-right-style": ["borderStyle"],
  5569.     "border-bottom-style": ["borderStyle"],
  5570.     "border-left-style": ["borderStyle"],
  5571.     "border-width": ["thickness"],
  5572.     "border-top-width": ["thickness"],
  5573.     "border-right-width": ["thickness"],
  5574.     "border-bottom-width": ["thickness"],
  5575.     "border-left-width": ["thickness"],
  5576.  
  5577.     "bottom": ["auto"],
  5578.     "caption-side": ["captionSide"],
  5579.     "clear": ["clear", "none"],
  5580.     "clip": ["auto"],
  5581.     "color": ["color", "systemColor"],
  5582.     "content": ["content", "none"],
  5583.     "counter-increment": ["none"],
  5584.     "counter-reset": ["none"],
  5585.     "cursor": ["cursor", "none"],
  5586.     "direction": ["direction"],
  5587.     "display": ["display", "none"],
  5588.     "empty-cells": [],
  5589.     "float": ["float", "none"],
  5590.     "font": ["fontStyle", "fontVariant", "fontWeight", "fontFamily"],
  5591.  
  5592.     "font-family": ["fontFamily"],
  5593.     "font-size": ["fontSize"],
  5594.     "font-size-adjust": [],
  5595.     "font-stretch": [],
  5596.     "font-style": ["fontStyle"],
  5597.     "font-variant": ["fontVariant"],
  5598.     "font-weight": ["fontWeight"],
  5599.  
  5600.     "height": ["auto"],
  5601.     "ime-mode": ["imeMode", "auto"],
  5602.     "left": ["auto"],
  5603.     "letter-spacing": [],
  5604.     "line-height": [],
  5605.  
  5606.     "list-style": ["listStyleType", "listStylePosition", "none"],
  5607.     "list-style-image": ["none"],
  5608.     "list-style-position": ["listStylePosition"],
  5609.     "list-style-type": ["listStyleType", "none"],
  5610.  
  5611.     "margin": [],
  5612.     "margin-top": [],
  5613.     "margin-right": [],
  5614.     "margin-bottom": [],
  5615.     "margin-left": [],
  5616.  
  5617.     "marker-offset": ["auto"],
  5618.     "min-height": ["none"],
  5619.     "max-height": ["none"],
  5620.     "min-width": ["width", "none"],
  5621.     "max-width": ["width", "none"],
  5622.  
  5623.     "opacity": [],
  5624.  
  5625.     "outline": ["borderStyle", "color", "systemColor", "none"],
  5626.     "outline-color": ["color", "systemColor"],
  5627.     "outline-style": ["borderStyle"],
  5628.     "outline-width": [],
  5629.  
  5630.     "overflow": ["overflow", "auto"],
  5631.     "overflow-x": ["overflow", "auto"],
  5632.     "overflow-y": ["overflow", "auto"],
  5633.  
  5634.     "padding": [],
  5635.     "padding-top": [],
  5636.     "padding-right": [],
  5637.     "padding-bottom": [],
  5638.     "padding-left": [],
  5639.  
  5640.     "position": ["position"],
  5641.     "quotes": ["none"],
  5642.     "right": ["auto"],
  5643.     "table-layout": ["tableLayout", "auto"],
  5644.     "text-align": ["textAlign"],
  5645.     "text-decoration": ["textDecoration", "none"],
  5646.     "text-indent": [],
  5647.     "text-rendering": ["textRendering", "auto"],
  5648.     "text-shadow": [],
  5649.     "text-transform": ["textTransform", "none"],
  5650.     "top": ["auto"],
  5651.     "unicode-bidi": [],
  5652.     "vertical-align": ["verticalAlign"],
  5653.     "visibility": ["visibility"],
  5654.     "white-space": ["whiteSpace"],
  5655.     "width": ["width", "auto"],
  5656.     "word-spacing": [],
  5657.     "word-wrap": ["wordWrap"],
  5658.     "z-index": [],
  5659.  
  5660.     "-moz-appearance": ["mozAppearance"],
  5661.     "-moz-border-image": ["mozBorderImage", "thickness", "none"],
  5662.     "-moz-border-radius": [],
  5663.     "-moz-border-radius-bottomleft": [],
  5664.     "-moz-border-radius-bottomright": [],
  5665.     "-moz-border-radius-topleft": [],
  5666.     "-moz-border-radius-topright": [],
  5667.     "-moz-border-top-colors": ["color", "systemColor"],
  5668.     "-moz-border-right-colors": ["color", "systemColor"],
  5669.     "-moz-border-bottom-colors": ["color", "systemColor"],
  5670.     "-moz-border-left-colors": ["color", "systemColor"],
  5671.     "-moz-border-start": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5672.     "-moz-border-end": ["borderStyle", "borderCollapse", "color", "systemColor", "none"],
  5673.     "-moz-border-start-color": ["color", "systemColor"],
  5674.     "-moz-border-end-color": ["color", "systemColor"],
  5675.     "-moz-border-start-style": ["borderStyle"],
  5676.     "-moz-border-end-style": ["borderStyle"],
  5677.     "-moz-border-start-width": ["thickness"],
  5678.     "-moz-border-end-width": ["thickness"],
  5679.     "-moz-box-align": ["mozBoxAlign"],
  5680.     "-moz-box-direction": ["mozBoxDirection"],
  5681.     "-moz-box-flex": [],
  5682.     "-moz-box-ordinal-group": [],
  5683.     "-moz-box-orient": ["mozBoxOrient"],
  5684.     "-moz-box-pack": ["mozBoxPack"],
  5685.     "-moz-box-shadow": ["mozBoxShadow", "none"],
  5686.     "-moz-box-sizing": ["mozBoxSizing"],
  5687.     "-moz-user-focus": ["userFocus", "none"],
  5688.     "-moz-user-input": ["userInput"],
  5689.     "-moz-user-modify": [],
  5690.     "-moz-user-select": ["userSelect", "none"],
  5691.     "-moz-background-clip": [],
  5692.     "-moz-background-inline-policy": [],
  5693.     "-moz-background-origin": [],
  5694.     "-moz-binding": [],
  5695.     "-moz-column-count": [],
  5696.     "-moz-column-gap": [],
  5697.     "-moz-column-rule": ["thickness", "borderStyle", "color", "systemColor"],
  5698.     "-moz-column-rule-width": ["thickness"],
  5699.     "-moz-column-rule-style": ["borderStyle"],
  5700.     "-moz-column-rule-color": ["color",  "systemColor"],
  5701.     "-moz-column-width": [],
  5702.     "-moz-image-region": [],
  5703.     "-moz-transform": ["mozTransformFunction", "none"],
  5704.     "-moz-transform-origin": ["bgPosition"]
  5705. };
  5706.  
  5707. this.inheritedStyleNames =
  5708. {
  5709.     "border-collapse": 1,
  5710.     "border-spacing": 1,
  5711.     "border-style": 1,
  5712.     "caption-side": 1,
  5713.     "color": 1,
  5714.     "cursor": 1,
  5715.     "direction": 1,
  5716.     "empty-cells": 1,
  5717.     "font": 1,
  5718.     "font-family": 1,
  5719.     "font-size-adjust": 1,
  5720.     "font-size": 1,
  5721.     "font-style": 1,
  5722.     "font-variant": 1,
  5723.     "font-weight": 1,
  5724.     "letter-spacing": 1,
  5725.     "line-height": 1,
  5726.     "list-style": 1,
  5727.     "list-style-image": 1,
  5728.     "list-style-position": 1,
  5729.     "list-style-type": 1,
  5730.     "opacity": 1,
  5731.     "quotes": 1,
  5732.     "text-align": 1,
  5733.     "text-decoration": 1,
  5734.     "text-indent": 1,
  5735.     "text-shadow": 1,
  5736.     "text-transform": 1,
  5737.     "white-space": 1,
  5738.     "word-spacing": 1,
  5739.     "word-wrap": 1
  5740. };
  5741.  
  5742. this.cssKeywords =
  5743. {
  5744.     "appearance":
  5745.     [
  5746.         "button",
  5747.         "button-small",
  5748.         "checkbox",
  5749.         "checkbox-container",
  5750.         "checkbox-small",
  5751.         "dialog",
  5752.         "listbox",
  5753.         "menuitem",
  5754.         "menulist",
  5755.         "menulist-button",
  5756.         "menulist-textfield",
  5757.         "menupopup",
  5758.         "progressbar",
  5759.         "radio",
  5760.         "radio-container",
  5761.         "radio-small",
  5762.         "resizer",
  5763.         "scrollbar",
  5764.         "scrollbarbutton-down",
  5765.         "scrollbarbutton-left",
  5766.         "scrollbarbutton-right",
  5767.         "scrollbarbutton-up",
  5768.         "scrollbartrack-horizontal",
  5769.         "scrollbartrack-vertical",
  5770.         "separator",
  5771.         "statusbar",
  5772.         "tab",
  5773.         "tab-left-edge",
  5774.         "tabpanels",
  5775.         "textfield",
  5776.         "toolbar",
  5777.         "toolbarbutton",
  5778.         "toolbox",
  5779.         "tooltip",
  5780.         "treeheadercell",
  5781.         "treeheadersortarrow",
  5782.         "treeitem",
  5783.         "treetwisty",
  5784.         "treetwistyopen",
  5785.         "treeview",
  5786.         "window"
  5787.     ],
  5788.  
  5789.     "systemColor":
  5790.     [
  5791.         "ActiveBorder",
  5792.         "ActiveCaption",
  5793.         "AppWorkspace",
  5794.         "Background",
  5795.         "ButtonFace",
  5796.         "ButtonHighlight",
  5797.         "ButtonShadow",
  5798.         "ButtonText",
  5799.         "CaptionText",
  5800.         "GrayText",
  5801.         "Highlight",
  5802.         "HighlightText",
  5803.         "InactiveBorder",
  5804.         "InactiveCaption",
  5805.         "InactiveCaptionText",
  5806.         "InfoBackground",
  5807.         "InfoText",
  5808.         "Menu",
  5809.         "MenuText",
  5810.         "Scrollbar",
  5811.         "ThreeDDarkShadow",
  5812.         "ThreeDFace",
  5813.         "ThreeDHighlight",
  5814.         "ThreeDLightShadow",
  5815.         "ThreeDShadow",
  5816.         "Window",
  5817.         "WindowFrame",
  5818.         "WindowText",
  5819.         "-moz-field",
  5820.         "-moz-fieldtext",
  5821.         "-moz-workspace",
  5822.         "-moz-visitedhyperlinktext",
  5823.         "-moz-nativehyperlinktext",
  5824.         "-moz-use-text-color"
  5825.     ],
  5826.  
  5827.     "color":
  5828.     [
  5829.         "AliceBlue",
  5830.         "AntiqueWhite",
  5831.         "Aqua",
  5832.         "Aquamarine",
  5833.         "Azure",
  5834.         "Beige",
  5835.         "Bisque",
  5836.         "Black",
  5837.         "BlanchedAlmond",
  5838.         "Blue",
  5839.         "BlueViolet",
  5840.         "Brown",
  5841.         "BurlyWood",
  5842.         "CadetBlue",
  5843.         "Chartreuse",
  5844.         "Chocolate",
  5845.         "Coral",
  5846.         "CornflowerBlue",
  5847.         "Cornsilk",
  5848.         "Crimson",
  5849.         "Cyan",
  5850.         "DarkBlue",
  5851.         "DarkCyan",
  5852.         "DarkGoldenRod",
  5853.         "DarkGray",
  5854.         "DarkGreen",
  5855.         "DarkKhaki",
  5856.         "DarkMagenta",
  5857.         "DarkOliveGreen",
  5858.         "DarkOrange",
  5859.         "DarkOrchid",
  5860.         "DarkRed",
  5861.         "DarkSalmon",
  5862.         "DarkSeaGreen",
  5863.         "DarkSlateBlue",
  5864.         "DarkSlateGray",
  5865.         "DarkTurquoise",
  5866.         "DarkViolet",
  5867.         "DeepPink",
  5868.         "DarkSkyBlue",
  5869.         "DimGray",
  5870.         "DodgerBlue",
  5871.         "Feldspar",
  5872.         "FireBrick",
  5873.         "FloralWhite",
  5874.         "ForestGreen",
  5875.         "Fuchsia",
  5876.         "Gainsboro",
  5877.         "GhostWhite",
  5878.         "Gold",
  5879.         "GoldenRod",
  5880.         "Gray",
  5881.         "Green",
  5882.         "GreenYellow",
  5883.         "HoneyDew",
  5884.         "HotPink",
  5885.         "IndianRed",
  5886.         "Indigo",
  5887.         "Ivory",
  5888.         "Khaki",
  5889.         "Lavender",
  5890.         "LavenderBlush",
  5891.         "LawnGreen",
  5892.         "LemonChiffon",
  5893.         "LightBlue",
  5894.         "LightCoral",
  5895.         "LightCyan",
  5896.         "LightGoldenRodYellow",
  5897.         "LightGrey",
  5898.         "LightGreen",
  5899.         "LightPink",
  5900.         "LightSalmon",
  5901.         "LightSeaGreen",
  5902.         "LightSkyBlue",
  5903.         "LightSlateBlue",
  5904.         "LightSlateGray",
  5905.         "LightSteelBlue",
  5906.         "LightYellow",
  5907.         "Lime",
  5908.         "LimeGreen",
  5909.         "Linen",
  5910.         "Magenta",
  5911.         "Maroon",
  5912.         "MediumAquaMarine",
  5913.         "MediumBlue",
  5914.         "MediumOrchid",
  5915.         "MediumPurple",
  5916.         "MediumSeaGreen",
  5917.         "MediumSlateBlue",
  5918.         "MediumSpringGreen",
  5919.         "MediumTurquoise",
  5920.         "MediumVioletRed",
  5921.         "MidnightBlue",
  5922.         "MintCream",
  5923.         "MistyRose",
  5924.         "Moccasin",
  5925.         "NavajoWhite",
  5926.         "Navy",
  5927.         "OldLace",
  5928.         "Olive",
  5929.         "OliveDrab",
  5930.         "Orange",
  5931.         "OrangeRed",
  5932.         "Orchid",
  5933.         "PaleGoldenRod",
  5934.         "PaleGreen",
  5935.         "PaleTurquoise",
  5936.         "PaleVioletRed",
  5937.         "PapayaWhip",
  5938.         "PeachPuff",
  5939.         "Peru",
  5940.         "Pink",
  5941.         "Plum",
  5942.         "PowderBlue",
  5943.         "Purple",
  5944.         "Red",
  5945.         "RosyBrown",
  5946.         "RoyalBlue",
  5947.         "SaddleBrown",
  5948.         "Salmon",
  5949.         "SandyBrown",
  5950.         "SeaGreen",
  5951.         "SeaShell",
  5952.         "Sienna",
  5953.         "Silver",
  5954.         "SkyBlue",
  5955.         "SlateBlue",
  5956.         "SlateGray",
  5957.         "Snow",
  5958.         "SpringGreen",
  5959.         "SteelBlue",
  5960.         "Tan",
  5961.         "Teal",
  5962.         "Thistle",
  5963.         "Tomato",
  5964.         "Turquoise",
  5965.         "Violet",
  5966.         "VioletRed",
  5967.         "Wheat",
  5968.         "White",
  5969.         "WhiteSmoke",
  5970.         "Yellow",
  5971.         "YellowGreen",
  5972.         "transparent",
  5973.         "invert"
  5974.     ],
  5975.  
  5976.     "auto":
  5977.     [
  5978.         "auto"
  5979.     ],
  5980.  
  5981.     "none":
  5982.     [
  5983.         "none"
  5984.     ],
  5985.  
  5986.     "captionSide":
  5987.     [
  5988.         "top",
  5989.         "bottom",
  5990.         "left",
  5991.         "right"
  5992.     ],
  5993.  
  5994.     "clear":
  5995.     [
  5996.         "left",
  5997.         "right",
  5998.         "both"
  5999.     ],
  6000.  
  6001.     "cursor":
  6002.     [
  6003.         "auto",
  6004.         "cell",
  6005.         "context-menu",
  6006.         "crosshair",
  6007.         "default",
  6008.         "help",
  6009.         "pointer",
  6010.         "progress",
  6011.         "move",
  6012.         "e-resize",
  6013.         "all-scroll",
  6014.         "ne-resize",
  6015.         "nw-resize",
  6016.         "n-resize",
  6017.         "se-resize",
  6018.         "sw-resize",
  6019.         "s-resize",
  6020.         "w-resize",
  6021.         "ew-resize",
  6022.         "ns-resize",
  6023.         "nesw-resize",
  6024.         "nwse-resize",
  6025.         "col-resize",
  6026.         "row-resize",
  6027.         "text",
  6028.         "vertical-text",
  6029.         "wait",
  6030.         "alias",
  6031.         "copy",
  6032.         "move",
  6033.         "no-drop",
  6034.         "not-allowed",
  6035.         "-moz-alias",
  6036.         "-moz-cell",
  6037.         "-moz-copy",
  6038.         "-moz-grab",
  6039.         "-moz-grabbing",
  6040.         "-moz-contextmenu",
  6041.         "-moz-zoom-in",
  6042.         "-moz-zoom-out",
  6043.         "-moz-spinning"
  6044.     ],
  6045.  
  6046.     "direction":
  6047.     [
  6048.         "ltr",
  6049.         "rtl"
  6050.     ],
  6051.  
  6052.     "bgAttachment":
  6053.     [
  6054.         "scroll",
  6055.         "fixed"
  6056.     ],
  6057.  
  6058.     "bgPosition":
  6059.     [
  6060.         "top",
  6061.         "center",
  6062.         "bottom",
  6063.         "left",
  6064.         "right"
  6065.     ],
  6066.  
  6067.     "bgRepeat":
  6068.     [
  6069.         "repeat",
  6070.         "repeat-x",
  6071.         "repeat-y",
  6072.         "no-repeat"
  6073.     ],
  6074.  
  6075.     "borderStyle":
  6076.     [
  6077.         "hidden",
  6078.         "dotted",
  6079.         "dashed",
  6080.         "solid",
  6081.         "double",
  6082.         "groove",
  6083.         "ridge",
  6084.         "inset",
  6085.         "outset",
  6086.         "-moz-bg-inset",
  6087.         "-moz-bg-outset",
  6088.         "-moz-bg-solid"
  6089.     ],
  6090.  
  6091.     "borderCollapse":
  6092.     [
  6093.         "collapse",
  6094.         "separate"
  6095.     ],
  6096.  
  6097.     "overflow":
  6098.     [
  6099.         "visible",
  6100.         "hidden",
  6101.         "scroll",
  6102.         "-moz-scrollbars-horizontal",
  6103.         "-moz-scrollbars-none",
  6104.         "-moz-scrollbars-vertical"
  6105.     ],
  6106.  
  6107.     "listStyleType":
  6108.     [
  6109.         "disc",
  6110.         "circle",
  6111.         "square",
  6112.         "decimal",
  6113.         "decimal-leading-zero",
  6114.         "lower-roman",
  6115.         "upper-roman",
  6116.         "lower-greek",
  6117.         "lower-alpha",
  6118.         "lower-latin",
  6119.         "upper-alpha",
  6120.         "upper-latin",
  6121.         "hebrew",
  6122.         "armenian",
  6123.         "georgian",
  6124.         "cjk-ideographic",
  6125.         "hiragana",
  6126.         "katakana",
  6127.         "hiragana-iroha",
  6128.         "katakana-iroha",
  6129.         "inherit"
  6130.     ],
  6131.  
  6132.     "listStylePosition":
  6133.     [
  6134.         "inside",
  6135.         "outside"
  6136.     ],
  6137.  
  6138.     "content":
  6139.     [
  6140.         "open-quote",
  6141.         "close-quote",
  6142.         "no-open-quote",
  6143.         "no-close-quote",
  6144.         "inherit"
  6145.     ],
  6146.  
  6147.     "fontStyle":
  6148.     [
  6149.         "normal",
  6150.         "italic",
  6151.         "oblique",
  6152.         "inherit"
  6153.     ],
  6154.  
  6155.     "fontVariant":
  6156.     [
  6157.         "normal",
  6158.         "small-caps",
  6159.         "inherit"
  6160.     ],
  6161.  
  6162.     "fontWeight":
  6163.     [
  6164.         "normal",
  6165.         "bold",
  6166.         "bolder",
  6167.         "lighter",
  6168.         "inherit"
  6169.     ],
  6170.  
  6171.     "fontSize":
  6172.     [
  6173.         "xx-small",
  6174.         "x-small",
  6175.         "small",
  6176.         "medium",
  6177.         "large",
  6178.         "x-large",
  6179.         "xx-large",
  6180.         "smaller",
  6181.         "larger"
  6182.     ],
  6183.  
  6184.     "fontFamily":
  6185.     [
  6186.         "Arial",
  6187.         "Comic Sans MS",
  6188.         "Georgia",
  6189.         "Tahoma",
  6190.         "Verdana",
  6191.         "Times New Roman",
  6192.         "Trebuchet MS",
  6193.         "Lucida Grande",
  6194.         "Helvetica",
  6195.         "serif",
  6196.         "sans-serif",
  6197.         "cursive",
  6198.         "fantasy",
  6199.         "monospace",
  6200.         "caption",
  6201.         "icon",
  6202.         "menu",
  6203.         "message-box",
  6204.         "small-caption",
  6205.         "status-bar",
  6206.         "inherit"
  6207.     ],
  6208.  
  6209.     "display":
  6210.     [
  6211.         "block",
  6212.         "inline",
  6213.         "inline-block",
  6214.         "list-item",
  6215.         "marker",
  6216.         "run-in",
  6217.         "compact",
  6218.         "table",
  6219.         "inline-table",
  6220.         "table-row-group",
  6221.         "table-column",
  6222.         "table-column-group",
  6223.         "table-header-group",
  6224.         "table-footer-group",
  6225.         "table-row",
  6226.         "table-cell",
  6227.         "table-caption",
  6228.         "-moz-box",
  6229.         "-moz-compact",
  6230.         "-moz-deck",
  6231.         "-moz-grid",
  6232.         "-moz-grid-group",
  6233.         "-moz-grid-line",
  6234.         "-moz-groupbox",
  6235.         "-moz-inline-block",
  6236.         "-moz-inline-box",
  6237.         "-moz-inline-grid",
  6238.         "-moz-inline-stack",
  6239.         "-moz-inline-table",
  6240.         "-moz-marker",
  6241.         "-moz-popup",
  6242.         "-moz-runin",
  6243.         "-moz-stack"
  6244.     ],
  6245.  
  6246.     "position":
  6247.     [
  6248.         "static",
  6249.         "relative",
  6250.         "absolute",
  6251.         "fixed",
  6252.         "inherit"
  6253.     ],
  6254.  
  6255.     "float":
  6256.     [
  6257.         "left",
  6258.         "right"
  6259.     ],
  6260.  
  6261.     "textAlign":
  6262.     [
  6263.         "left",
  6264.         "right",
  6265.         "center",
  6266.         "justify"
  6267.     ],
  6268.  
  6269.     "tableLayout":
  6270.     [
  6271.         "fixed"
  6272.     ],
  6273.  
  6274.     "textDecoration":
  6275.     [
  6276.         "underline",
  6277.         "overline",
  6278.         "line-through",
  6279.         "blink"
  6280.     ],
  6281.  
  6282.     "textTransform":
  6283.     [
  6284.         "capitalize",
  6285.         "lowercase",
  6286.         "uppercase",
  6287.         "inherit"
  6288.     ],
  6289.  
  6290.     "unicodeBidi":
  6291.     [
  6292.         "normal",
  6293.         "embed",
  6294.         "bidi-override"
  6295.     ],
  6296.  
  6297.     "visibility":
  6298.     [
  6299.         "visible",
  6300.         "hidden",
  6301.         "collapse",
  6302.         "inherit"
  6303.     ],
  6304.  
  6305.     "whiteSpace":
  6306.     [
  6307.         "normal",
  6308.         "pre",
  6309.         "nowrap",
  6310.         "pre-wrap",
  6311.         "pre-line",
  6312.         "inherit"
  6313.     ],
  6314.  
  6315.     "verticalAlign":
  6316.     [
  6317.         "baseline",
  6318.         "sub",
  6319.         "super",
  6320.         "top",
  6321.         "text-top",
  6322.         "middle",
  6323.         "bottom",
  6324.         "text-bottom",
  6325.         "inherit"
  6326.     ],
  6327.  
  6328.     "thickness":
  6329.     [
  6330.         "thin",
  6331.         "medium",
  6332.         "thick"
  6333.     ],
  6334.  
  6335.     "userFocus":
  6336.     [
  6337.         "ignore",
  6338.         "normal"
  6339.     ],
  6340.  
  6341.     "userInput":
  6342.     [
  6343.         "disabled",
  6344.         "enabled"
  6345.     ],
  6346.  
  6347.     "userSelect":
  6348.     [
  6349.         "normal"
  6350.     ],
  6351.  
  6352.     "mozBoxSizing":
  6353.     [
  6354.         "content-box",
  6355.         "padding-box",
  6356.         "border-box"
  6357.     ],
  6358.  
  6359.     "mozBoxAlign":
  6360.     [
  6361.         "start",
  6362.         "center",
  6363.         "end",
  6364.         "baseline",
  6365.         "stretch"
  6366.     ],
  6367.  
  6368.     "mozBoxDirection":
  6369.     [
  6370.         "normal",
  6371.         "reverse"
  6372.     ],
  6373.  
  6374.     "mozBoxOrient":
  6375.     [
  6376.         "horizontal",
  6377.         "vertical"
  6378.     ],
  6379.  
  6380.     "mozBoxPack":
  6381.     [
  6382.         "start",
  6383.         "center",
  6384.         "end"
  6385.     ],
  6386.  
  6387.     "mozBoxShadow":
  6388.     [
  6389.         "inset"
  6390.     ],
  6391.  
  6392.     "mozBorderImage":
  6393.     [
  6394.         "stretch",
  6395.         "round",
  6396.         "repeat"
  6397.     ],
  6398.  
  6399.     "mozTransformFunction":
  6400.     [
  6401.         "matrix",
  6402.         "rotate",
  6403.         "scale",
  6404.         "scaleX",
  6405.         "scaleY",
  6406.         "skew",
  6407.         "skewX",
  6408.         "skewY",
  6409.         "translate",
  6410.         "translateX",
  6411.         "translateY"
  6412.     ],
  6413.  
  6414.     "width":
  6415.     [
  6416.         "-moz-max-content",
  6417.         "-moz-min-content",
  6418.         "-moz-fit-content",
  6419.         "-moz-available"
  6420.     ],
  6421.  
  6422.     "imeMode":
  6423.     [
  6424.         "normal",
  6425.         "active",
  6426.         "inactive",
  6427.         "disabled"
  6428.     ],
  6429.  
  6430.     "textRendering":
  6431.     [
  6432.         "optimizeSpeed",
  6433.         "optimizeLegibility",
  6434.         "geometricPrecision"
  6435.     ],
  6436.  
  6437.     "wordWrap":
  6438.     [
  6439.         "normal",
  6440.         "break-word",
  6441.         "inherit"
  6442.     ]
  6443. };
  6444.  
  6445. this.nonEditableTags =
  6446. {
  6447.     "HTML": 1,
  6448.     "HEAD": 1,
  6449.     "html": 1,
  6450.     "head": 1
  6451. };
  6452.  
  6453. this.innerEditableTags =
  6454. {
  6455.     "BODY": 1,
  6456.     "body": 1
  6457. };
  6458.  
  6459. this.selfClosingTags =
  6460. { // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML
  6461.     "meta": 1,
  6462.     "link": 1,
  6463.     "area": 1,
  6464.     "base": 1,
  6465.     "col": 1,
  6466.     "input": 1,
  6467.     "img": 1,
  6468.     "br": 1,
  6469.     "hr": 1,
  6470.     "param":1,
  6471.     "embed":1
  6472. };
  6473.  
  6474. const invisibleTags = this.invisibleTags =
  6475. {
  6476.     "HTML": 1,
  6477.     "HEAD": 1,
  6478.     "TITLE": 1,
  6479.     "META": 1,
  6480.     "LINK": 1,
  6481.     "STYLE": 1,
  6482.     "SCRIPT": 1,
  6483.     "NOSCRIPT": 1,
  6484.     "BR": 1,
  6485.     "PARAM": 1,
  6486.     "COL": 1,
  6487.  
  6488.     "html": 1,
  6489.     "head": 1,
  6490.     "title": 1,
  6491.     "meta": 1,
  6492.     "link": 1,
  6493.     "style": 1,
  6494.     "script": 1,
  6495.     "noscript": 1,
  6496.     "br": 1,
  6497.     "param": 1,
  6498.     "col": 1,
  6499.     /*
  6500.     "window": 1,
  6501.     "browser": 1,
  6502.     "frame": 1,
  6503.     "tabbrowser": 1,
  6504.     "WINDOW": 1,
  6505.     "BROWSER": 1,
  6506.     "FRAME": 1,
  6507.     "TABBROWSER": 1,
  6508.     */
  6509. };
  6510.  
  6511. // ************************************************************************************************
  6512. // Debug Logging
  6513.  
  6514. function ERROR(exc)
  6515. {
  6516.         ddd("FIREBUG WARNING: " + exc);
  6517. }
  6518. this.ERROR = ERROR;
  6519.  
  6520. function ddd(text)
  6521. {
  6522.     consoleService.logStringMessage(text + "");
  6523. }
  6524. this.ddd = ddd;
  6525.  
  6526. function deprecated(msg, fnc)
  6527. {
  6528.     return function deprecationWrapper()
  6529.     {
  6530.         if (!this.nagged)
  6531.         {
  6532.             var explain = "Deprecated function ("+fnc.name+") "+msg;
  6533.             var caller = Components.stack.caller;  // drop frame with deprecated()
  6534.             ERROR(explain+" "+ caller.toString());
  6535.  
  6536.             debugger;
  6537.             this.nagged = true;
  6538.         }
  6539.         return fnc.apply(this, arguments);
  6540.     }
  6541. }
  6542. this.deprecated = deprecated;
  6543.  
  6544. // ************************************************************************************************
  6545. // Math Utils
  6546.  
  6547. this.formatNumber = function(number)
  6548. {
  6549.     number += "";
  6550.     var x = number.split(".");
  6551.     var x1 = x[0];
  6552.     var x2 = x.length > 1 ? "." + x[1] : "";
  6553.     var rgx = /(\d+)(\d{3})/;
  6554.     while (rgx.test(x1))
  6555.         x1 = x1.replace(rgx, "$1" + "," + "$2");
  6556.     return x1 + x2;
  6557. }
  6558.  
  6559. // ************************************************************************************************
  6560. // File Size Utils
  6561.  
  6562. this.formatSize = function(bytes)
  6563. {
  6564.     // Get size precision (number of decimal places from the preferences)
  6565.     // and make sure it's within limits.
  6566.     var sizePrecision = Firebug.sizePrecision;
  6567.     sizePrecision = (sizePrecision > 2) ? 2 : sizePrecision;
  6568.     sizePrecision = (sizePrecision < -1) ? -1 : sizePrecision;
  6569.  
  6570.     if (sizePrecision == -1)
  6571.         return bytes + " B";
  6572.  
  6573.     var a = Math.pow(10, sizePrecision);
  6574.  
  6575.     if (bytes == -1 || bytes == undefined)
  6576.         return "?";
  6577.     else if (bytes == 0)
  6578.         return "0";
  6579.     else if (bytes < 1024)
  6580.         return bytes + " B";
  6581.     else if (bytes < (1024*1024))
  6582.         return Math.round((bytes/1024)*a)/a + " KB";
  6583.     else
  6584.         return Math.round((bytes/(1024*1024))*a)/a + " MB";
  6585. }
  6586.  
  6587. // ************************************************************************************************
  6588. // Time Utils
  6589.  
  6590. this.formatTime = function(elapsed)
  6591. {
  6592.     if (elapsed == -1)
  6593.         return "";
  6594.     else if (elapsed == 0)
  6595.         return "0";
  6596.     else if (elapsed < 1000)
  6597.         return elapsed + "ms";
  6598.     else if (elapsed < 60000)
  6599.         return (Math.round(elapsed/10) / 100) + "s";
  6600.     else
  6601.     {
  6602.         var min = Math.floor(elapsed/60000);
  6603.         var sec = (elapsed % 60000);
  6604.         return min + "m " + (Math.round((elapsed/1000)%60)) + "s";
  6605.     }
  6606. }
  6607.  
  6608. // ************************************************************************************************
  6609.  
  6610. this.ReversibleIterator = function(length, start, reverse)
  6611. {
  6612.     this.length = length;
  6613.     this.index = start;
  6614.     this.reversed = !!reverse;
  6615.  
  6616.     this.next = function() {
  6617.         if (this.index === undefined || this.index === null) {
  6618.             this.index = this.reversed ? length : -1;
  6619.         }
  6620.         this.index += this.reversed ? -1 : 1;
  6621.  
  6622.         return 0 <= this.index && this.index < length;
  6623.     };
  6624.     this.reverse = function() {
  6625.         this.reversed = !this.reversed;
  6626.     };
  6627. };
  6628.  
  6629. /**
  6630.  * @class Implements a RegExp-like object that will search for the literal value
  6631.  * of a given string, rather than the regular expression. This allows for
  6632.  * iterative literal searches without having to escape user input strings
  6633.  * to prevent invalid regular expressions from being used.
  6634.  *
  6635.  * @constructor
  6636.  * @param {String} literal Text to search for
  6637.  * @param {Boolean} reverse Truthy to preform a reverse search, falsy to perform a forward seach
  6638.  * @param {Boolean} caseSensitive Truthy to perform a case sensitive search, falsy to perform a case insensitive search.
  6639.  */
  6640. this.LiteralRegExp = function(literal, reverse, caseSensitive)
  6641. {
  6642.     var searchToken = (!caseSensitive) ? literal.toLowerCase() : literal;
  6643.  
  6644.     this.__defineGetter__("global", function() { return true; });
  6645.     this.__defineGetter__("multiline", function() { return true; });
  6646.     this.__defineGetter__("reverse", function() { return reverse; });
  6647.     this.__defineGetter__("ignoreCase", function() { return !caseSensitive; });
  6648.     this.lastIndex = 0;
  6649.  
  6650.     this.exec = function(text)
  6651.     {
  6652.         if (!text)
  6653.             return null;
  6654.  
  6655.         var searchText = (!caseSensitive) ? text.toLowerCase() : text,
  6656.             startIndex = (reverse ? text.length-1 : 0) + this.lastIndex,
  6657.             index;
  6658.  
  6659.         if (0 <= startIndex && startIndex < text.length)
  6660.             index = searchText[reverse ? "lastIndexOf" : "indexOf"](searchToken, startIndex);
  6661.         else
  6662.             index = -1;
  6663.  
  6664.         if (index >= 0)
  6665.         {
  6666.             var ret = [ text.substr(index, searchToken.length) ];
  6667.             ret.index = index;
  6668.             ret.input = text;
  6669.             this.lastIndex = index + (reverse ? -1*text.length : searchToken.length);
  6670.             return ret;
  6671.         }
  6672.         else
  6673.             this.lastIndex = 0;
  6674.  
  6675.         return null;
  6676.     };
  6677.     this.test = function(text)
  6678.     {
  6679.         if (!text)
  6680.             return false;
  6681.  
  6682.         var searchText = (!caseSensitive) ? text.toLowerCase() : text;
  6683.         return searchText.indexOf(searchToken) >= 0;
  6684.     };
  6685. };
  6686.  
  6687. this.ReversibleRegExp = function(regex, flags)
  6688. {
  6689.     var re = {};
  6690.  
  6691.     function expression(text, reverse) {
  6692.         return text + (reverse ? "(?![\\s\\S]*" + text + ")" : "");
  6693.     }
  6694.     function flag(flags, caseSensitive) {
  6695.         return (flags || "") + (caseSensitive ? "" : "i");
  6696.     }
  6697.  
  6698.     this.exec = function(text, reverse, caseSensitive, lastMatch)
  6699.     {
  6700.         // Ensure we have a regex
  6701.         var key = (reverse ? "r" : "n") + (caseSensitive ? "n" : "i");
  6702.         if (!re[key])
  6703.         {
  6704.             try
  6705.             {
  6706.                 re[key] = new RegExp(expression(regex, reverse), flag(flags, caseSensitive));
  6707.             }
  6708.             catch (ex)
  6709.             {
  6710.                 // The user likely entered an invalid regular expression or is in the
  6711.                 // process of entering a valid one. Treat this as a plain text search
  6712.                 re[key] = new FBL.LiteralRegExp(regex, reverse, caseSensitive);
  6713.             }
  6714.         }
  6715.  
  6716.         // Modify as needed to all for iterative searches
  6717.         var indexOffset = 0;
  6718.         var searchText = text;
  6719.         if (lastMatch) {
  6720.             if (reverse) {
  6721.                 searchText = text.substr(0, lastMatch.index);
  6722.             } else {
  6723.                 indexOffset = lastMatch.index+lastMatch[0].length;
  6724.                 searchText = text.substr(indexOffset);
  6725.             }
  6726.         }
  6727.  
  6728.         var curRe = re[key];
  6729.         curRe.lastIndex = 0;
  6730.         var ret = curRe.exec(searchText);
  6731.         if (ret) {
  6732.             ret.input = text;
  6733.             ret.index = ret.index + indexOffset;
  6734.             ret.reverse = reverse;
  6735.             ret.caseSensitive = caseSensitive;
  6736.         }
  6737.         return ret;
  6738.     };
  6739. };
  6740.  
  6741. function unwrapObject(object)
  6742. {
  6743.     // TODO: We might be able to make this check more authoritative with QueryInterface.
  6744.     if (typeof(object) === 'undefined' || object == null)
  6745.         return object;
  6746.  
  6747.     if (object.wrappedJSObject)
  6748.         return object.wrappedJSObject;
  6749.  
  6750.     return object;
  6751. }
  6752. this.unwrapObject = unwrapObject;
  6753.  
  6754. this.unwrapIValue = function(object)
  6755. {
  6756.     var unwrapped = object.getWrappedValue();
  6757.     try
  6758.     {
  6759.         return XPCSafeJSObjectWrapper(unwrapped);  // this should be the only call to getWrappedValue in firebug
  6760.     }
  6761.     catch (exc)
  6762.     {
  6763.     }
  6764. }
  6765.  
  6766. }).apply(FBL);
  6767. } catch(e) /*@explore*/
  6768. { /*@explore*/
  6769.     dump("FBL Fails "+e+"\n"); /*@explore*/
  6770.     dump("If the service @joehewitt.com/firebug;1 fails, try deleting compreg.dat, xpti.dat\n"); /*@explore*/
  6771.     dump("Another cause can be mangled install.rdf.\n"); /*@explore*/
  6772. } /*@explore*/
  6773.